mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-16 07:35:16 +00:00
Compare commits
184 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 26890e94eb | |||
| 55c83f9a59 | |||
| b837d82878 | |||
| cbdcdb1e0f | |||
| 4fea85289e | |||
| ba5f9132c9 | |||
| 991681daa2 | |||
| 2faf53e16b | |||
| 5430ed99de | |||
| fef4dfd05e | |||
| 05943db3b5 | |||
| 682fabd1ef | |||
| ee8ccc6d7e | |||
| 8ee0bcac2a | |||
| 0c4ad74788 | |||
| 7d944e4f1f | |||
| 67b2394c16 | |||
| 269d08a35f | |||
| f626e83e48 | |||
| 8ba18cd587 | |||
| 54c12fea89 | |||
| afb5ce71ce | |||
| 6fef6acf10 | |||
| a27411401b | |||
| 3718fc319b | |||
| c6c9463f8c | |||
| b410685318 | |||
| b742b29259 | |||
| df5d27ab61 | |||
| 2870155c0b | |||
| 9ba1075f48 | |||
| 4ae5b2e050 | |||
| 4369bd8f5e | |||
| 321b24a7b9 | |||
| 22e0c1fb54 | |||
| ba65e3c020 | |||
| c19ab952e3 | |||
| ecc29087b5 | |||
| 87e056de64 | |||
| 761200529f | |||
| 770351916e | |||
| 4b7d957b47 | |||
| a1c03c18a5 | |||
| 5d376b054f | |||
| 277d13c2bb | |||
| 4a9389d80a | |||
| 33593c13ae | |||
| eef82c18c2 | |||
| 7363580095 | |||
| 23a46b0597 | |||
| 51c79f294c | |||
| ce1e0e1873 | |||
| c2775b3b63 | |||
| bcb224d1cb | |||
| 8bc5f22866 | |||
| c3f2ef02c4 | |||
| 08e2c90d9d | |||
| d2bb2ba454 | |||
| ba912318cd | |||
| 3d1f057373 | |||
| 0679807517 | |||
| 7f0e4803e2 | |||
| fa76029a17 | |||
| 376c68957f | |||
| 222ec8d4b9 | |||
| 90715c1944 | |||
| 801e1b6059 | |||
| 14bb659b75 | |||
| eab3c50288 | |||
| 74396e7240 | |||
| 6c2539e886 | |||
| aba25d8521 | |||
| f0c97756de | |||
| f14c57d4c2 | |||
| b6f60cc8ad | |||
| 170516d343 | |||
| c931e91bef | |||
| db9151ae36 | |||
| f2d86b6d69 | |||
| 534cdd091b | |||
| fdf4a48638 | |||
| 58a3c5634e | |||
| f0ddb6e89f | |||
| 2b408db7cb | |||
| 4e4bd23e2b | |||
| 62d0eb606e | |||
| 0e9beeae14 | |||
| 5e4dbdee24 | |||
| 9435dae6cd | |||
| 813210bf47 | |||
| c372dfc544 | |||
| ac77200683 | |||
| f5afbc90a4 | |||
| b24760c819 | |||
| 6732957e42 | |||
| 1fd0006859 | |||
| 5a205346e3 | |||
| e193004659 | |||
| fe3a9c33d0 | |||
| d83116b88f | |||
| 5f56098ae7 | |||
| 0f935f4372 | |||
| df5c5a8c01 | |||
| 57a0c3bad8 | |||
| 2a52326a45 | |||
| 87e4ee1eeb | |||
| 45833eea9a | |||
| baf10e9b15 | |||
| 463a947df1 | |||
| cceebb4fd9 | |||
| afd5465a61 | |||
| fe604733de | |||
| da22f9f39f | |||
| 284ebbf75d | |||
| 2c08ccad87 | |||
| fa4e234bab | |||
| 35c4179ca1 | |||
| c14ba7ce34 | |||
| 601ce3add3 | |||
| 08ff39fd0b | |||
| 82a6ee3875 | |||
| 3bf592f870 | |||
| 241e205717 | |||
| fb2720ea8d | |||
| 14853befda | |||
| e7f2803d11 | |||
| 320015dd8f | |||
| 471c124a98 | |||
| aa44a109c6 | |||
| 41e5ca8422 | |||
| 53af8b2bb6 | |||
| bd885b135f | |||
| 62a6dde81a | |||
| 14b72b344f | |||
| ac03668063 | |||
| 2512e176c9 | |||
| b613393866 | |||
| 607a6e95de | |||
| 871f574ff7 | |||
| 377e470b15 | |||
| e0a3f0cea5 | |||
| 66727b2736 | |||
| 8215ad977f | |||
| a611dffbdf | |||
| 39f378d8cf | |||
| f00da58153 | |||
| 09b038b16d | |||
| cf601d97ad | |||
| 80edad53d4 | |||
| a972b32769 | |||
| efcaae3e60 | |||
| e0ee591638 | |||
| 310a6d909f | |||
| cefd8fc50f | |||
| a4422593f2 | |||
| e2c5d22756 | |||
| 84385d9dc3 | |||
| 6cfa4c8ab7 | |||
| c76ccc53e1 | |||
| d48eafb1a2 | |||
| 302d9b4b91 | |||
| a091a7f68b | |||
| 53621f6de3 | |||
| 288b965db3 | |||
| 578c9dcfc6 | |||
| 64de17b248 | |||
| d699dff872 | |||
| 501843d624 | |||
| 508ce4283e | |||
| 4aa8e66549 | |||
| 917a6f70d8 | |||
| 2f5e53da55 | |||
| c3cdefc90a | |||
| 9c580e4989 | |||
| dbc75f7352 | |||
| db841a8578 | |||
| 50c902c4e4 | |||
| 84b6e0bde6 | |||
| abc29c3fba | |||
| 8356c534f5 | |||
| 78ff2687b4 | |||
| 9b6586fb36 | |||
| ea133219d0 | |||
| c9b5d81a3c |
@@ -0,0 +1 @@
|
||||
theme/**/*
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"rules": {
|
||||
"indent": [ 2, 4 ],
|
||||
"quotes": [ 2, "single" ],
|
||||
"linebreak-style": [ 2, "unix" ],
|
||||
"semi": [ 2, "always" ],
|
||||
"no-unused-vars": [ 2, {
|
||||
"vars": "all",
|
||||
"args": "none"
|
||||
} ],
|
||||
"spaced-comment": [ 2, "always" ]
|
||||
},
|
||||
"env": {
|
||||
"node": true,
|
||||
"mocha": true,
|
||||
"browser": true
|
||||
},
|
||||
"extends": "eslint:recommended"
|
||||
}
|
||||
@@ -24,8 +24,5 @@ build/Release
|
||||
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
|
||||
node_modules
|
||||
|
||||
theme/javascript/vendors
|
||||
theme/vendors
|
||||
|
||||
# vim swapfile
|
||||
*.swp
|
||||
|
||||
+3
-1
@@ -1,7 +1,9 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- "4.1"
|
||||
- "0.12"
|
||||
- "0.11"
|
||||
- "0.10"
|
||||
before_install:
|
||||
- npm install svgexport -g
|
||||
- npm install svgexport -g
|
||||
|
||||
+21
-14
@@ -21,32 +21,39 @@ Translators
|
||||
------------
|
||||
|
||||
- German
|
||||
- Winnie (@winniehell)
|
||||
- Winnie (@winniehell)
|
||||
- Italian
|
||||
- Giulio Bonanome (@gbonanome)
|
||||
- Giulio Bonanome (@gbonanome)
|
||||
- Russian
|
||||
- Andrey Akinshin (@AndreyAkinshin)
|
||||
- Andrey Akinshin (@AndreyAkinshin)
|
||||
- Norwegian
|
||||
- Knut Melvær (@kmelve)
|
||||
- Knut Melvær (@kmelve)
|
||||
- Persian (Farsi)
|
||||
- Mohammad Hossein Mojtahedi (@mhm5000)
|
||||
- Mohammad Hossein Mojtahedi (@mhm5000)
|
||||
- Polish
|
||||
- Łukasz Szeremeta (@lszeremeta)
|
||||
- Łukasz Szeremeta (@lszeremeta)
|
||||
- Portuguese
|
||||
- Ryan O'Mullan (@RyanOM)
|
||||
- Ryan O'Mullan (@RyanOM)
|
||||
- Spanish
|
||||
- Ryan O'Mullan (@RyanOM)
|
||||
- Ryan O'Mullan (@RyanOM)
|
||||
- Simplifiled Chinese
|
||||
- Hu Hao (@howiehu)
|
||||
- Hu Hao (@howiehu)
|
||||
- Traditional Chinese
|
||||
- Hu Hao (@howiehu)
|
||||
- Hu Hao (@howiehu)
|
||||
- French
|
||||
- Samy Pessé (@SamyPesse)
|
||||
- Samy Pessé (@SamyPesse)
|
||||
- Romanian
|
||||
- Iancu Aurel (@awrelll)
|
||||
- Finnish
|
||||
- Tommi Savikko (@savikko)
|
||||
- Tommi Savikko (@savikko)
|
||||
- Japanese
|
||||
- Iancu Aurel (@awrelll)
|
||||
- Iancu Aurel (@awrelll)
|
||||
- Korean
|
||||
- Iancu Aurel (@awrelll)
|
||||
- Iancu Aurel (@awrelll)
|
||||
- SangYeob Yu (@deminoth)
|
||||
- Vietnamese
|
||||
- Hong Nguyen (@nghong)
|
||||
- Hebrew
|
||||
- @a-moses
|
||||
- Ukrainian
|
||||
- @Karnaukhov
|
||||
|
||||
+85
@@ -2,6 +2,91 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 2.5.0-beta.6
|
||||
- Fix default style for pdf footer and header
|
||||
- Correctly handle query in image urls
|
||||
- Fix templating in imported content
|
||||
- Use helvetica as default font for PDFs
|
||||
|
||||
## 2.5.0-beta.5
|
||||
- Fix position of sidebar toggle button
|
||||
- Update asciidoc parser to remove git dependency
|
||||
|
||||
## 2.5.0-beta.4
|
||||
- Fix compatibility for some plugins (like `autocover`)
|
||||
- Fix position of sidebar toggle button when glossary button is present
|
||||
|
||||
## 2.5.0-beta.3
|
||||
- Fix installation of plugins when using a pre-release
|
||||
|
||||
## 2.5.0-beta.2
|
||||
- Fix support for pre-releases
|
||||
|
||||
## 2.5.0-beta.1
|
||||
- Font settings, sharing and search are externalized as default plugins
|
||||
- Plugins can define a configuration schema in the manifest, this schema will be used to validate configuration during build
|
||||
- New Node.js API for plugin: `book.formatString(type, content)`
|
||||
- New client side API for website plugins: `gitbook.toolbar.createButton(opts)`
|
||||
- Better header/footer for PDF, CSS wil be inlined to easily style the header/footer
|
||||
- Cleaner table of contents for ebooks
|
||||
- Support for RTL in ebook's table of contents
|
||||
- Better colors for mobi (links and code blocks)
|
||||
|
||||
## 2.4.3
|
||||
- Add ukrainian translation (`uk`)
|
||||
- Add `book.json` configuration for maximum size of search index
|
||||
- Improve reliability of summary parser
|
||||
|
||||
## 2.4.2
|
||||
- Default plugins should not be installed by `gitbook install`
|
||||
- Limit search index size to avoid crash during generation
|
||||
- Fix code highlighting for html without language specified
|
||||
- Fix warning message for gitbook version when building a multilingual book
|
||||
|
||||
## 2.4.1
|
||||
- Fix disabling of default plugins, ex: `-highlight`
|
||||
|
||||
## 2.4.0
|
||||
- Fix page being updated when user wants to open a link in a new tab
|
||||
- Plugins can now replaced default code highlighter
|
||||
- Add semantic information for screen readers (web version)
|
||||
- Content references accept absolute paths, resolved to book folder
|
||||
- Improve overall reliability
|
||||
|
||||
## 2.3.3
|
||||
- Fix bug in SUMMARY parsing preventing multiple entries without filenames
|
||||
|
||||
## 2.3.2
|
||||
- Fix blocks (like maths) in Asciidoc
|
||||
- Fix error when checking gitbook version
|
||||
|
||||
## 2.3.1
|
||||
- Fix black font color for ebooks (mobi, pdf and epub)
|
||||
- Fix ISO code for korean language
|
||||
- Fix korean translation
|
||||
- Fix syntax highlighting for asciidoc
|
||||
- Fix inline html escaping in markdown
|
||||
- Add warning for file outside SUMMARY
|
||||
- Force SUMMARY entries to be unique by filename
|
||||
|
||||
## 2.3.0
|
||||
- Fix nunjucks issue with multiple `{% raw %}` blocks
|
||||
- Fix crash when git conref failed
|
||||
- Fix crash when failed to download remote image (better error message)
|
||||
- Fix flicking effect when changing page (big UX improvement)
|
||||
- Add Hebrew translation (`he`)
|
||||
- Add utility method `book.config.get` for plugins
|
||||
- Hooks `page:before` and `page` are no longer deprecated
|
||||
- Remove webfonts to make website lighter
|
||||
- Make glossary's order case insensitive
|
||||
|
||||
## 2.2.0
|
||||
- Fix direction in code blocks (always LTR)
|
||||
- Add options `chapterMark` and `pageBreaksBefore` for PDF
|
||||
- Update code highlighting library
|
||||
- `book.json` accessible as `config` in templating syntax
|
||||
- Add Vietnamese translation (`vi`)
|
||||
|
||||
## 2.1.0
|
||||
- Fix error in calcul of `levels` in table of contents, error introduced a few versions ago
|
||||
- Add optional `styles/print.css` to replace `print.css` used in ebook
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
module.exports = function (grunt) {
|
||||
var path = require("path");
|
||||
|
||||
// Load NPM tasks
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-less');
|
||||
grunt.loadNpmTasks('grunt-contrib-requirejs');
|
||||
grunt.loadNpmTasks("grunt-bower-install-simple");
|
||||
|
||||
// Init GRUNT configuraton
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
'bower-install-simple': {
|
||||
options: {
|
||||
color: true,
|
||||
production: false,
|
||||
directory: "theme/vendors"
|
||||
}
|
||||
},
|
||||
less: {
|
||||
development: {
|
||||
options: {
|
||||
compress: true,
|
||||
yuicompress: true,
|
||||
optimization: 2
|
||||
},
|
||||
files: {
|
||||
"theme/assets/style.css": "theme/stylesheets/website.less",
|
||||
"theme/assets/print.css": "theme/stylesheets/ebook.less"
|
||||
}
|
||||
}
|
||||
},
|
||||
requirejs: {
|
||||
compile: {
|
||||
options: {
|
||||
name: "gitbook",
|
||||
baseUrl: "theme/javascript/",
|
||||
out: "theme/assets/app.js",
|
||||
preserveLicenseComments: false,
|
||||
optimize: "uglify",
|
||||
include: ["requireLib"],
|
||||
paths: {
|
||||
"jQuery": '../vendors/jquery/dist/jquery',
|
||||
"lodash": '../vendors/lodash/dist/lodash',
|
||||
"requireLib": '../vendors/requirejs/require',
|
||||
"Mousetrap": '../vendors/mousetrap/mousetrap',
|
||||
"lunr": '../vendors/lunr.js/lunr',
|
||||
"URIjs": '../vendors/URIjs/src/',
|
||||
"ace": '../vendors/ace-builds/src-noconflict/'
|
||||
},
|
||||
shim: {
|
||||
'jQuery': {
|
||||
exports: '$'
|
||||
},
|
||||
'lodash': {
|
||||
exports: '_'
|
||||
},
|
||||
'Mousetrap': {
|
||||
exports: 'Mousetrap'
|
||||
},
|
||||
'lunr': {
|
||||
exports: 'lunr'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
copy: {
|
||||
vendors: {
|
||||
files: [
|
||||
{
|
||||
expand: true,
|
||||
cwd: 'theme/vendors/fontawesome/fonts/',
|
||||
src: ['**'],
|
||||
dest: 'theme/assets/fonts/fontawesome/',
|
||||
filter: 'isFile'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask("bower-install", [ "bower-install-simple" ]);
|
||||
|
||||
// Build
|
||||
grunt.registerTask('build', [
|
||||
'bower-install',
|
||||
'less',
|
||||
'requirejs',
|
||||
'copy:vendors'
|
||||
]);
|
||||
|
||||
grunt.registerTask('default', [
|
||||
'build'
|
||||
]);
|
||||
};
|
||||
@@ -2,14 +2,15 @@ GitBook
|
||||
=======
|
||||
|
||||
[](http://badge.fury.io/js/gitbook)
|
||||
[](https://travis-ci.org/GitbookIO/gitbook)
|
||||
[](https://ci.appveyor.com/project/SamyPesse/gitbook)
|
||||
[](https://travis-ci.org/GitbookIO/gitbook)
|
||||
[](https://ci.appveyor.com/project/GitBook/gitbook)
|
||||
[](https://slack.gitbook.com)
|
||||
|
||||
GitBook is a command line tool (and Node.js library) for building beautiful books using GitHub/Git and Markdown (or AsciiDoc). Here is an example: [Learn Javascript](https://www.gitbook.com/book/GitBookIO/javascript).
|
||||
|
||||
You can publish and host book easily online using [gitbook.com](https://www.gitbook.com), a web-editor is [also available](https://www.gitbook.com/editor).
|
||||
You can publish and host book easily online using [gitbook.com](https://www.gitbook.com), a desktop editor is [also available](https://www.gitbook.com/editor).
|
||||
|
||||
Stay updated by following [@GitBookIO](https://twitter.com/GitBookIO) on Twitter or [GitBook](https://www.facebook.com/gitbookcom) on Facebook.
|
||||
Check out the [GitBook Community Slack Channel](https://slack.gitbook.com), Stay updated by following [@GitBookIO](https://twitter.com/GitBookIO) on Twitter or [GitBook](https://www.facebook.com/gitbookcom) on Facebook.
|
||||
|
||||
Complete documentation is available at [help.gitbook.com](http://help.gitbook.com/).
|
||||
|
||||
@@ -50,7 +51,7 @@ $ gitbook build
|
||||
* [AsciiDoc Support](http://help.gitbook.com/format/asciidoc.html)
|
||||
* [Variables and Templating](http://help.gitbook.com/format/templating.html)
|
||||
* [Content References](http://help.gitbook.com/format/conrefs.html)
|
||||
* [Plugins](http://help.gitbook.com/plugins/index.html)
|
||||
* [Plugins](http://help.gitbook.com/format/plugins.html)
|
||||
* Interractive reader website:
|
||||
* Search
|
||||
* Font Settings (Serif, Sans Serif)
|
||||
@@ -221,9 +222,9 @@ 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
|
||||
$ gitbook versions:link ./gitbook
|
||||
```
|
||||
|
||||
Now the version tag `2.0.0` wil be associated with the `./gitbook` folder.
|
||||
Now `gitbook-cli` will be using the `./gitbook` folder.
|
||||
|
||||
You can uninstall it using: `gitbook versions:uninstall 2.0.0`.
|
||||
You can uninstall it using: `gitbook versions:uninstall latest`.
|
||||
|
||||
+3
-3
@@ -2,6 +2,6 @@
|
||||
|
||||
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 installed this package globally, you need to uninstall it."));
|
||||
console.log(color.red(">> Run 'npm uninstall -g gitbook' then 'npm install -g gitbook-cli'"));
|
||||
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 installed this package globally, you need to uninstall it.'));
|
||||
console.log(color.red('>> Run "npm uninstall -g gitbook" then "npm install -g gitbook-cli"'));
|
||||
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"name": "GitBook",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"jquery": "2.1.1",
|
||||
"lodash": "2.4.1",
|
||||
"requirejs": "2.1.11",
|
||||
"URIjs": "1.13.1",
|
||||
"mousetrap": "1.4.6",
|
||||
"lunr.js": "0.5.2",
|
||||
"fontawesome": "4.1.0"
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
var _ = require('lodash');
|
||||
var gulp = require('gulp');
|
||||
var gutil = require('gulp-util');
|
||||
var less = require('gulp-less');
|
||||
var rename = require('gulp-rename');
|
||||
var minifyCSS = require('gulp-minify-css');
|
||||
var browserify = require('browserify');
|
||||
var mergeStream = require('merge-stream');
|
||||
var source = require('vinyl-source-stream');
|
||||
|
||||
gulp.task('css', function() {
|
||||
var merged = mergeStream();
|
||||
|
||||
_.each({
|
||||
'ebook.less': 'ebook/ebook.css',
|
||||
'pdf.less': 'ebook/pdf.css',
|
||||
'mobi.less': 'ebook/mobi.css',
|
||||
'epub.less': 'ebook/epub.css',
|
||||
'website.less': 'website/style.css'
|
||||
}, function(out, input) {
|
||||
gutil.log('compiling', input, 'into', out);
|
||||
merged.add(gulp.src('theme/stylesheets/'+input)
|
||||
.pipe(less())
|
||||
.pipe(minifyCSS())
|
||||
.pipe(rename(out))
|
||||
.pipe(gulp.dest('theme/assets/')));
|
||||
});
|
||||
|
||||
return merged;
|
||||
});
|
||||
|
||||
gulp.task('js', function() {
|
||||
return browserify('./theme/javascript/index.js')
|
||||
.bundle()
|
||||
.pipe(source('app.js'))
|
||||
.pipe(gulp.dest('./theme/assets/website'));
|
||||
});
|
||||
|
||||
gulp.task('assets', function() {
|
||||
return gulp.src('./node_modules/font-awesome/fonts/*')
|
||||
.pipe(gulp.dest('theme/assets/website/fonts/fontawesome/'));
|
||||
});
|
||||
|
||||
gulp.task('default', ['css', 'js', 'assets'], function() {
|
||||
|
||||
});
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = {
|
||||
// Return non-parsed html
|
||||
// since blocks are by default non-parsable, a simple identity method works fine
|
||||
html: _.identity,
|
||||
|
||||
// Highlight a code block
|
||||
// This block can be extent by plugins
|
||||
code: _.identity
|
||||
};
|
||||
+147
-203
@@ -1,30 +1,24 @@
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var path = require("path");
|
||||
var util = require("util");
|
||||
var lunr = require("lunr");
|
||||
var parsers = require("gitbook-parsers");
|
||||
var color = require("bash-color");
|
||||
var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
var path = require('path');
|
||||
var parsers = require('gitbook-parsers');
|
||||
|
||||
var fs = require("./utils/fs");
|
||||
var parseNavigation = require("./utils/navigation");
|
||||
var parseProgress = require("./utils/progress");
|
||||
var pageUtil = require("./utils/page");
|
||||
var batch = require("./utils/batch");
|
||||
var links = require("./utils/links");
|
||||
var i18n = require("./utils/i18n");
|
||||
var logger = require("./utils/logger");
|
||||
var fs = require('./utils/fs');
|
||||
var parseNavigation = require('./utils/navigation');
|
||||
var parseProgress = require('./utils/progress');
|
||||
var pageUtil = require('./utils/page');
|
||||
var pathUtil = require('./utils/path');
|
||||
var links = require('./utils/links');
|
||||
var i18n = require('./utils/i18n');
|
||||
var logger = require('./utils/logger');
|
||||
|
||||
var Configuration = require("./configuration");
|
||||
var TemplateEngine = require("./template");
|
||||
var Plugin = require("./plugin");
|
||||
var PluginsList = require("./pluginslist");
|
||||
var Configuration = require('./configuration');
|
||||
var TemplateEngine = require('./template');
|
||||
var PluginsList = require('./pluginslist');
|
||||
|
||||
var generators = require("./generators");
|
||||
var generators = require('./generators');
|
||||
|
||||
var Book = function(root, context, parent) {
|
||||
var that = this;
|
||||
|
||||
this.context = _.defaults(context || {}, {
|
||||
// Extend book configuration
|
||||
config: {},
|
||||
@@ -35,7 +29,7 @@ var Book = function(root, context, parent) {
|
||||
},
|
||||
|
||||
// Log level
|
||||
logLevel: "info"
|
||||
logLevel: 'info'
|
||||
});
|
||||
|
||||
// Log
|
||||
@@ -49,7 +43,7 @@ var Book = function(root, context, parent) {
|
||||
|
||||
// Configuration
|
||||
this.config = new Configuration(this, this.context.config);
|
||||
Object.defineProperty(this, "options", {
|
||||
Object.defineProperty(this, 'options', {
|
||||
get: function () {
|
||||
return this.config.options;
|
||||
}
|
||||
@@ -83,21 +77,13 @@ var Book = function(root, context, parent) {
|
||||
this.readmeFile = null;
|
||||
this.langsFile = null;
|
||||
|
||||
// Search Index
|
||||
this.searchIndex = lunr(function () {
|
||||
this.ref('url');
|
||||
|
||||
this.field('title', { boost: 10 });
|
||||
this.field('body');
|
||||
});
|
||||
|
||||
// Bind methods
|
||||
_.bindAll(this);
|
||||
};
|
||||
|
||||
// Return string representation
|
||||
Book.prototype.toString = function() {
|
||||
return "[Book "+this.root+"]";
|
||||
return '[Book '+this.root+']';
|
||||
};
|
||||
|
||||
// Initialize and parse the book: config, summary, glossary
|
||||
@@ -115,11 +101,11 @@ Book.prototype.parse = function() {
|
||||
return that.parseLangs()
|
||||
.then(function() {
|
||||
multilingual = that.langs.length > 0;
|
||||
if (multilingual) that.log.info.ln("Parsing multilingual book, with", that.langs.length, "languages");
|
||||
if (multilingual) that.log.info.ln('Parsing multilingual book, with', that.langs.length, 'languages');
|
||||
|
||||
// Sub-books that inherit from the current book configuration
|
||||
that.books = _.map(that.langs, function(lang) {
|
||||
that.log.info.ln("Preparing language book", lang.lang);
|
||||
that.log.info.ln('Preparing language book', lang.lang);
|
||||
return new Book(
|
||||
path.join(that.root, lang.path),
|
||||
_.merge({}, that.context, {
|
||||
@@ -129,7 +115,7 @@ Book.prototype.parse = function() {
|
||||
})
|
||||
}),
|
||||
that
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -168,15 +154,15 @@ Book.prototype.generate = function(generator) {
|
||||
var that = this;
|
||||
that.options.generator = generator || that.options.generator;
|
||||
|
||||
that.log.info.ln("start generation with", that.options.generator, "generator");
|
||||
that.log.info.ln('start generation with', that.options.generator, 'generator');
|
||||
return Q()
|
||||
|
||||
// Clean output folder
|
||||
.then(function() {
|
||||
that.log.info("clean", that.options.generator, "generator");
|
||||
that.log.info('clean', that.options.generator, 'generator');
|
||||
return fs.clean(that.options.output)
|
||||
.progress(function(p) {
|
||||
that.log.debug.ln("remove", p.file, "("+p.i+"/"+p.count+")");
|
||||
that.log.debug.ln('remove', p.file, '('+p.i+'/'+p.count+')');
|
||||
})
|
||||
.then(function() {
|
||||
that.log.info.ok();
|
||||
@@ -186,12 +172,20 @@ Book.prototype.generate = function(generator) {
|
||||
// Create generator
|
||||
.then(function() {
|
||||
var Generator = generators[generator];
|
||||
if (!Generator) throw "Generator '"+that.options.generator+"' doesn't exist";
|
||||
if (!Generator) throw 'Generator \''+that.options.generator+'\' doesn\'t exist';
|
||||
generator = new Generator(that);
|
||||
|
||||
return generator.prepare();
|
||||
})
|
||||
|
||||
// Transform configuration
|
||||
.then(function() {
|
||||
return that.callHook('config', that.config.dump())
|
||||
.then(function(newConfig) {
|
||||
that.config.replace(newConfig);
|
||||
});
|
||||
})
|
||||
|
||||
// Generate content
|
||||
.then(function() {
|
||||
if (that.isMultilingual()) {
|
||||
@@ -199,12 +193,12 @@ Book.prototype.generate = function(generator) {
|
||||
} else {
|
||||
// Separate list of files into the different operations needed
|
||||
var ops = _.groupBy(that.files, function(file) {
|
||||
if (file[file.length -1] == "/") {
|
||||
return "directories";
|
||||
if (file[file.length -1] == '/') {
|
||||
return 'directories';
|
||||
} else if (_.contains(parsers.extensions, path.extname(file)) && that.navigation[file]) {
|
||||
return "content";
|
||||
return 'content';
|
||||
} else {
|
||||
return "files";
|
||||
return 'files';
|
||||
}
|
||||
});
|
||||
|
||||
@@ -213,9 +207,9 @@ Book.prototype.generate = function(generator) {
|
||||
|
||||
// First, let's create folder
|
||||
.then(function() {
|
||||
return _.reduce(ops["directories"] || [], function(prev, folder) {
|
||||
return _.reduce(ops.directories || [], function(prev, folder) {
|
||||
return prev.then(function() {
|
||||
that.log.debug.ln("transferring folder", folder);
|
||||
that.log.debug.ln('transferring folder', folder);
|
||||
return Q(generator.transferFolder(folder));
|
||||
});
|
||||
}, Q());
|
||||
@@ -223,19 +217,19 @@ Book.prototype.generate = function(generator) {
|
||||
|
||||
// Then, let's copy other files
|
||||
.then(function() {
|
||||
return Q.all(_.map(ops["files"] || [], function(file) {
|
||||
that.log.debug.ln("transferring file", file);
|
||||
return Q.all(_.map(ops.files || [], function(file) {
|
||||
that.log.debug.ln('transferring file', file);
|
||||
return Q(generator.transferFile(file));
|
||||
}));
|
||||
})
|
||||
|
||||
// Finally let's generate content
|
||||
.then(function() {
|
||||
var nFiles = (ops["content"] || []).length;
|
||||
return _.reduce(ops["content"] || [], function(prev, file, i) {
|
||||
var nFiles = (ops.content || []).length;
|
||||
return _.reduce(ops.content || [], function(prev, file, i) {
|
||||
return prev.then(function() {
|
||||
var p = ((i*100)/nFiles).toFixed(0)+"%";
|
||||
that.log.debug.ln("processing", file, p);
|
||||
var p = ((i*100)/nFiles).toFixed(0)+'%';
|
||||
that.log.debug.ln('processing', file, p);
|
||||
|
||||
return Q(generator.convertFile(file))
|
||||
.fail(function(err) {
|
||||
@@ -252,21 +246,21 @@ Book.prototype.generate = function(generator) {
|
||||
|
||||
// Finish generation
|
||||
.then(function() {
|
||||
return generator.callHook("finish:before");
|
||||
return that.callHook('finish:before');
|
||||
})
|
||||
.then(function() {
|
||||
return generator.finish();
|
||||
})
|
||||
.then(function() {
|
||||
return generator.callHook("finish");
|
||||
return that.callHook('finish');
|
||||
})
|
||||
.then(function() {
|
||||
that.log.info.ln("generation is finished");
|
||||
that.log.info.ln('generation is finished');
|
||||
});
|
||||
};
|
||||
|
||||
// Generate the output for a multilingual book
|
||||
Book.prototype.generateMultiLingual = function(generator) {
|
||||
Book.prototype.generateMultiLingual = function() {
|
||||
var that = this;
|
||||
|
||||
return Q()
|
||||
@@ -287,26 +281,26 @@ Book.prototype.generateFile = function(output, options) {
|
||||
options = _.defaults(options || {}, {
|
||||
ebookFormat: path.extname(output).slice(1)
|
||||
});
|
||||
output = output || path.resolve(book.root, "book."+options.ebookFormat);
|
||||
output = output || path.resolve(book.root, 'book.'+options.ebookFormat);
|
||||
|
||||
return fs.tmp.dir()
|
||||
.then(function(tmpDir) {
|
||||
book.setOutput(tmpDir);
|
||||
|
||||
return book.generate(options.ebookFormat)
|
||||
.then(function(_options) {
|
||||
.then(function() {
|
||||
var copyFile = function(lang) {
|
||||
var _outputFile = output;
|
||||
var _tmpDir = tmpDir;
|
||||
|
||||
if (lang) {
|
||||
_outputFile = _outputFile.slice(0, -path.extname(_outputFile).length)+"_"+lang+path.extname(_outputFile);
|
||||
_outputFile = _outputFile.slice(0, -path.extname(_outputFile).length)+'_'+lang+path.extname(_outputFile);
|
||||
_tmpDir = path.join(_tmpDir, lang);
|
||||
}
|
||||
|
||||
book.log.debug.ln("copy ebook to", _outputFile);
|
||||
book.log.debug.ln('copy ebook to', _outputFile);
|
||||
return fs.copy(
|
||||
path.join(_tmpDir, "index."+options.ebookFormat),
|
||||
path.join(_tmpDir, 'index.'+options.ebookFormat),
|
||||
_outputFile
|
||||
);
|
||||
};
|
||||
@@ -326,7 +320,7 @@ Book.prototype.generateFile = function(output, options) {
|
||||
}
|
||||
})
|
||||
.then(function(n) {
|
||||
book.log.info.ok(n+" file(s) generated");
|
||||
book.log.info.ok(n+' file(s) generated');
|
||||
|
||||
return fs.remove(tmpDir);
|
||||
});
|
||||
@@ -338,7 +332,7 @@ Book.prototype.generateFile = function(output, options) {
|
||||
Book.prototype.parseConfig = function() {
|
||||
var that = this;
|
||||
|
||||
that.log.info("loading book configuration....")
|
||||
that.log.info('loading book configuration....');
|
||||
return that.config.load()
|
||||
.then(function() {
|
||||
that.log.info.ok();
|
||||
@@ -348,39 +342,38 @@ Book.prototype.parseConfig = function() {
|
||||
// Parse list of plugins
|
||||
Book.prototype.parsePlugins = function() {
|
||||
var that = this;
|
||||
var failed = [];
|
||||
|
||||
// Load plugins
|
||||
return that.plugins.load(that.options.plugins)
|
||||
.then(function() {
|
||||
if (_.size(that.plugins.failed) > 0) return Q.reject(new Error("Error loading plugins: "+that.plugins.failed.join(",")+". Run 'gitbook install' to install plugins from NPM."));
|
||||
if (_.size(that.plugins.failed) > 0) return Q.reject(new Error('Error loading plugins: '+that.plugins.failed.join(',')+'. Run \'gitbook install\' to install plugins from NPM.'));
|
||||
|
||||
that.log.info.ok(that.plugins.count()+" plugins loaded");
|
||||
that.log.debug.ln("normalize plugins list");
|
||||
that.log.info.ok(that.plugins.count()+' plugins loaded');
|
||||
that.log.debug.ln('normalize plugins list');
|
||||
});
|
||||
};
|
||||
|
||||
// Parse readme to extract defaults title and description
|
||||
Book.prototype.parseReadme = function() {
|
||||
var that = this;
|
||||
var structure = that.config.getStructure("readme");
|
||||
that.log.debug.ln("start parsing readme:", structure);
|
||||
var structure = that.config.getStructure('readme');
|
||||
that.log.debug.ln('start parsing readme:', structure);
|
||||
|
||||
return that.findFile(structure)
|
||||
.then(function(readme) {
|
||||
if (!readme) throw "No README file";
|
||||
if (!_.contains(that.files, readme.path)) throw "README file is ignored";
|
||||
if (!readme) throw 'No README file';
|
||||
if (!_.contains(that.files, readme.path)) throw 'README file is ignored';
|
||||
|
||||
that.readmeFile = readme.path;
|
||||
that._defaultsStructure(that.readmeFile);
|
||||
|
||||
that.log.debug.ln("readme located at", that.readmeFile);
|
||||
that.log.debug.ln('readme located at', that.readmeFile);
|
||||
return that.template.renderFile(that.readmeFile)
|
||||
.then(function(content) {
|
||||
return readme.parser.readme(content)
|
||||
.fail(function(err) {
|
||||
throw that.normError(err, {
|
||||
name: err.name || "Readme Parse Error",
|
||||
name: err.name || 'Readme Parse Error',
|
||||
fileName: that.readmeFile
|
||||
});
|
||||
});
|
||||
@@ -397,8 +390,8 @@ Book.prototype.parseReadme = function() {
|
||||
Book.prototype.parseLangs = function() {
|
||||
var that = this;
|
||||
|
||||
var structure = that.config.getStructure("langs");
|
||||
that.log.debug.ln("start parsing languages index:", structure);
|
||||
var structure = that.config.getStructure('langs');
|
||||
that.log.debug.ln('start parsing languages index:', structure);
|
||||
|
||||
return that.findFile(structure)
|
||||
.then(function(langs) {
|
||||
@@ -407,13 +400,13 @@ Book.prototype.parseLangs = function() {
|
||||
that.langsFile = langs.path;
|
||||
that._defaultsStructure(that.langsFile);
|
||||
|
||||
that.log.debug.ln("languages index located at", that.langsFile);
|
||||
that.log.debug.ln('languages index located at', that.langsFile);
|
||||
return that.template.renderFile(that.langsFile)
|
||||
.then(function(content) {
|
||||
return langs.parser.langs(content)
|
||||
.fail(function(err) {
|
||||
throw that.normError(err, {
|
||||
name: err.name || "Langs Parse Error",
|
||||
name: err.name || 'Langs Parse Error',
|
||||
fileName: that.langsFile
|
||||
});
|
||||
});
|
||||
@@ -428,19 +421,19 @@ Book.prototype.parseLangs = function() {
|
||||
Book.prototype.parseSummary = function() {
|
||||
var that = this;
|
||||
|
||||
var structure = that.config.getStructure("summary");
|
||||
that.log.debug.ln("start parsing summary:", structure);
|
||||
var structure = that.config.getStructure('summary');
|
||||
that.log.debug.ln('start parsing summary:', structure);
|
||||
|
||||
return that.findFile(structure)
|
||||
.then(function(summary) {
|
||||
if (!summary) throw "No SUMMARY file";
|
||||
if (!summary) throw 'No SUMMARY file';
|
||||
|
||||
// Remove the summary from the list of files to parse
|
||||
that.summaryFile = summary.path;
|
||||
that._defaultsStructure(that.summaryFile);
|
||||
that.files = _.without(that.files, that.summaryFile);
|
||||
|
||||
that.log.debug.ln("summary located at", that.summaryFile);
|
||||
that.log.debug.ln('summary located at', that.summaryFile);
|
||||
return that.template.renderFile(that.summaryFile)
|
||||
.then(function(content) {
|
||||
return summary.parser.summary(content, {
|
||||
@@ -450,7 +443,7 @@ Book.prototype.parseSummary = function() {
|
||||
})
|
||||
.fail(function(err) {
|
||||
throw that.normError(err, {
|
||||
name: err.name || "Summary Parse Error",
|
||||
name: err.name || 'Summary Parse Error',
|
||||
fileName: that.summaryFile
|
||||
});
|
||||
});
|
||||
@@ -466,8 +459,8 @@ Book.prototype.parseSummary = function() {
|
||||
Book.prototype.parseGlossary = function() {
|
||||
var that = this;
|
||||
|
||||
var structure = that.config.getStructure("glossary");
|
||||
that.log.debug.ln("start parsing glossary: ", structure);
|
||||
var structure = that.config.getStructure('glossary');
|
||||
that.log.debug.ln('start parsing glossary: ', structure);
|
||||
|
||||
return that.findFile(structure)
|
||||
.then(function(glossary) {
|
||||
@@ -478,13 +471,13 @@ Book.prototype.parseGlossary = function() {
|
||||
that._defaultsStructure(that.glossaryFile);
|
||||
that.files = _.without(that.files, that.glossaryFile);
|
||||
|
||||
that.log.debug.ln("glossary located at", that.glossaryFile);
|
||||
that.log.debug.ln('glossary located at', that.glossaryFile);
|
||||
return that.template.renderFile(that.glossaryFile)
|
||||
.then(function(content) {
|
||||
return glossary.parser.glossary(content)
|
||||
.fail(function(err) {
|
||||
throw that.normError(err, {
|
||||
name: err.name || "Glossary Parse Error",
|
||||
name: err.name || 'Glossary Parse Error',
|
||||
fileName: that.glossaryFile
|
||||
});
|
||||
});
|
||||
@@ -512,16 +505,16 @@ Book.prototype.parsePage = function(filename, options) {
|
||||
var interpolate = function(fn) {
|
||||
return Q(fn(page))
|
||||
.then(function(_page) {
|
||||
page = _page;
|
||||
page = _page || page;
|
||||
});
|
||||
};
|
||||
|
||||
that.log.debug.ln("start parsing file", filename);
|
||||
that.log.debug.ln('start parsing file', filename);
|
||||
|
||||
var extension = path.extname(filename);
|
||||
var filetype = parsers.get(extension);
|
||||
|
||||
if (!filetype) return Q.reject(new Error("Can't parse file: "+filename));
|
||||
if (!filetype) return Q.reject(new Error('Can\'t parse file: '+filename));
|
||||
|
||||
// Type of parser used
|
||||
page.type = filetype.name;
|
||||
@@ -535,7 +528,7 @@ Book.prototype.parsePage = function(filename, options) {
|
||||
// Progress in the book
|
||||
page.progress = parseProgress(that.navigation, filename);
|
||||
|
||||
that.log.debug.ln("render template", filename);
|
||||
that.log.debug.ln('render template', filename);
|
||||
|
||||
// Read file content
|
||||
return that.readFile(page.path)
|
||||
@@ -562,7 +555,7 @@ Book.prototype.parsePage = function(filename, options) {
|
||||
.then(function(content) {
|
||||
page.content = content;
|
||||
|
||||
that.log.debug.ln("use file parser", filetype.name, "for", filename);
|
||||
that.log.debug.ln('use file parser', filetype.name, 'for', filename);
|
||||
return filetype.page(page.content);
|
||||
})
|
||||
|
||||
@@ -570,11 +563,11 @@ Book.prototype.parsePage = function(filename, options) {
|
||||
.then(function(_page) {
|
||||
return _.reduce(_page.sections, function(prev, section) {
|
||||
return prev.then(function(_sections) {
|
||||
return that.template.postProcess(section.content || "")
|
||||
return that.template.postProcess(section.content || '')
|
||||
.then(function(content) {
|
||||
section.content = content;
|
||||
return _sections.concat([section]);
|
||||
})
|
||||
});
|
||||
});
|
||||
}, Q([]));
|
||||
})
|
||||
@@ -599,7 +592,6 @@ Book.prototype.parsePage = function(filename, options) {
|
||||
})
|
||||
|
||||
.then(function() {
|
||||
that.indexPage(page);
|
||||
return page;
|
||||
});
|
||||
};
|
||||
@@ -622,29 +614,52 @@ Book.prototype.findFile = function(filename) {
|
||||
parser: parsers.get(ext),
|
||||
path: filepath
|
||||
};
|
||||
})
|
||||
});
|
||||
});
|
||||
}, Q(null));
|
||||
};
|
||||
|
||||
// Format a string using a specific markup language
|
||||
Book.prototype.formatString = function(extension, content) {
|
||||
return Q()
|
||||
.then(function() {
|
||||
var filetype = parsers.get(extension);
|
||||
if (!filetype) throw new Error('Filetype doesn\'t exist: '+filetype);
|
||||
|
||||
return filetype.page(content);
|
||||
})
|
||||
|
||||
// Merge sections
|
||||
.then(function(page) {
|
||||
return _.reduce(page.sections, function(content, section) {
|
||||
return content + section.content;
|
||||
}, '');
|
||||
});
|
||||
};
|
||||
|
||||
// Check if a file exists in the book
|
||||
Book.prototype.fileExists = function(filename) {
|
||||
return fs.exists(
|
||||
path.join(this.root, filename)
|
||||
this.resolve(filename)
|
||||
);
|
||||
};
|
||||
|
||||
// Check if a file path is inside the book
|
||||
Book.prototype.fileIsInBook = function(filename) {
|
||||
return pathUtil.isInRoot(this.root, filename);
|
||||
};
|
||||
|
||||
// Read a file
|
||||
Book.prototype.readFile = function(filename) {
|
||||
return fs.readFile(
|
||||
path.join(this.root, filename),
|
||||
{ encoding: "utf8" }
|
||||
this.resolve(filename),
|
||||
{ encoding: 'utf8' }
|
||||
);
|
||||
};
|
||||
|
||||
// Return stat for a file
|
||||
Book.prototype.statFile = function(filename) {
|
||||
return fs.stat(path.join(this.root, filename));
|
||||
return fs.stat(this.resolve(filename));
|
||||
};
|
||||
|
||||
// List all files in the book
|
||||
@@ -682,7 +697,7 @@ Book.prototype.listAllFiles = function() {
|
||||
};
|
||||
|
||||
// Return true if the book is a multilingual book
|
||||
Book.prototype.isMultilingual = function(filename) {
|
||||
Book.prototype.isMultilingual = function() {
|
||||
return this.books.length > 0;
|
||||
};
|
||||
|
||||
@@ -702,40 +717,38 @@ Book.prototype.isEntryPoint = function(fp) {
|
||||
return fp == this.readmeFile;
|
||||
};
|
||||
|
||||
// Resolve a path in book
|
||||
Book.prototype.resolve = function(p) {
|
||||
return path.resolve(this.root, p);
|
||||
// Alias to book.config.get
|
||||
Book.prototype.getConfig = function(key, def) {
|
||||
return this.config.get(key, def);
|
||||
};
|
||||
|
||||
// Resolve a path in the book source
|
||||
// Enforce that the output path in the root folder
|
||||
Book.prototype.resolve = function() {
|
||||
return pathUtil.resolveInRoot.apply(null, [this.root].concat(_.toArray(arguments)));
|
||||
};
|
||||
|
||||
// Convert an abslute path into a relative path to this
|
||||
Book.prototype.relative = function(p) {
|
||||
return path.relative(this.root, p);
|
||||
};
|
||||
|
||||
// Normalize a path to .html and convert README -> index
|
||||
Book.prototype.contentPath = function(link) {
|
||||
if (
|
||||
path.basename(link, path.extname(link)) == "README"
|
||||
|| link == this.readmeFile
|
||||
path.basename(link, path.extname(link)) == 'README' ||
|
||||
link == this.readmeFile
|
||||
) {
|
||||
link = path.join(path.dirname(link), "index"+path.extname(link));
|
||||
link = path.join(path.dirname(link), 'index'+path.extname(link));
|
||||
}
|
||||
|
||||
link = links.changeExtension(link, ".html");
|
||||
link = links.changeExtension(link, '.html');
|
||||
return link;
|
||||
}
|
||||
};
|
||||
|
||||
// Normalize a link to .html and convert README -> index
|
||||
Book.prototype.contentLink = function(link) {
|
||||
return links.normalize(this.contentPath(link));
|
||||
}
|
||||
|
||||
// Index a page into the search index
|
||||
Book.prototype.indexPage = function(page) {
|
||||
var nav = this.navigation[page.path];
|
||||
if (!nav) return;
|
||||
|
||||
this.log.debug.ln("index page", page.path);
|
||||
this.searchIndex.add({
|
||||
url: this.contentLink(page.path),
|
||||
title: nav.title,
|
||||
body: pageUtil.extractText(page.sections),
|
||||
});
|
||||
};
|
||||
|
||||
// Default structure paths to an extension
|
||||
@@ -743,11 +756,11 @@ Book.prototype._defaultsStructure = function(filename) {
|
||||
var that = this;
|
||||
var extension = path.extname(filename);
|
||||
|
||||
that.readmeFile = that.readmeFile || that.config.getStructure("readme")+extension;
|
||||
that.summaryFile = that.summaryFile || that.config.getStructure("summary")+extension;
|
||||
that.glossaryFile = that.glossaryFile || that.config.getStructure("glossary")+extension;
|
||||
that.langsFile = that.langsFile || that.config.getStructure("langs")+extension;
|
||||
}
|
||||
that.readmeFile = that.readmeFile || that.config.getStructure('readme')+extension;
|
||||
that.summaryFile = that.summaryFile || that.config.getStructure('summary')+extension;
|
||||
that.glossaryFile = that.glossaryFile || that.config.getStructure('glossary')+extension;
|
||||
that.langsFile = that.langsFile || that.config.getStructure('langs')+extension;
|
||||
};
|
||||
|
||||
// Change output path
|
||||
Book.prototype.setOutput = function(p) {
|
||||
@@ -760,7 +773,7 @@ Book.prototype.setOutput = function(p) {
|
||||
};
|
||||
|
||||
// Translate a strign according to the book language
|
||||
Book.prototype.i18n = function(phrase) {
|
||||
Book.prototype.i18n = function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
return i18n.__.apply({}, [this.config.normalizeLanguage()].concat(args));
|
||||
};
|
||||
@@ -779,87 +792,18 @@ Book.prototype.normError = function(err, opts, defs) {
|
||||
err.toString = function() {
|
||||
var attributes = [];
|
||||
|
||||
if (this.fileName) attributes.push("In file '"+this.fileName+"'");
|
||||
if (this.lineNumber) attributes.push("Line "+this.lineNumber);
|
||||
if (this.columnNumber) attributes.push("Column "+this.columnNumber);
|
||||
return (this.name || "Error")+": "+this.message+((attributes.length > 0)? " ("+attributes.join(", ")+")" : "")
|
||||
if (this.fileName) attributes.push('In file \''+this.fileName+'\'');
|
||||
if (this.lineNumber) attributes.push('Line '+this.lineNumber);
|
||||
if (this.columnNumber) attributes.push('Column '+this.columnNumber);
|
||||
return (this.name || 'Error')+': '+this.message+((attributes.length > 0)? ' ('+attributes.join(', ')+')' : '');
|
||||
};
|
||||
|
||||
return err;
|
||||
};
|
||||
|
||||
// Init and return a book
|
||||
Book.init = function(root, opts) {
|
||||
var book = new Book(root, opts);
|
||||
var extensionToUse = ".md";
|
||||
|
||||
var chaptersPaths = function(chapters) {
|
||||
return _.reduce(chapters || [], function(accu, chapter) {
|
||||
var o = {
|
||||
title: chapter.title
|
||||
};
|
||||
if (chapter.path) o.path = chapter.path;
|
||||
|
||||
return accu.concat(
|
||||
[o].concat(chaptersPaths(chapter.articles))
|
||||
);
|
||||
}, []);
|
||||
};
|
||||
|
||||
book.log.info.ln("init book at", root);
|
||||
return fs.mkdirp(root)
|
||||
.then(function() {
|
||||
book.log.info.ln("detect structure from SUMMARY (if it exists)");
|
||||
return book.parseSummary();
|
||||
})
|
||||
.fail(function() {
|
||||
return Q();
|
||||
})
|
||||
.then(function() {
|
||||
var summary = book.summaryFile || "SUMMARY.md";
|
||||
var chapters = book.summary.chapters || [];
|
||||
extensionToUse = path.extname(summary);
|
||||
|
||||
if (chapters.length == 0) {
|
||||
chapters = [
|
||||
{
|
||||
title: "Summary",
|
||||
path: "SUMMARY"+extensionToUse
|
||||
},
|
||||
{
|
||||
title: "Introduction",
|
||||
path: "README"+extensionToUse
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
return Q(chaptersPaths(chapters));
|
||||
})
|
||||
.then(function(chapters) {
|
||||
// Create files that don't exist
|
||||
return Q.all(_.map(chapters, function(chapter) {
|
||||
if (!chapter.path) return Q();
|
||||
var absolutePath = path.resolve(book.root, chapter.path);
|
||||
|
||||
return fs.exists(absolutePath)
|
||||
.then(function(exists) {
|
||||
if(exists) {
|
||||
book.log.info.ln("found", chapter.path);
|
||||
return;
|
||||
} else {
|
||||
book.log.info.ln("create", chapter.path);
|
||||
}
|
||||
|
||||
return fs.mkdirp(path.dirname(absolutePath))
|
||||
.then(function() {
|
||||
return fs.writeFile(absolutePath, '# '+chapter.title+'\n');
|
||||
});
|
||||
});
|
||||
}));
|
||||
})
|
||||
.then(function() {
|
||||
book.log.info.ln("initialization is finished");
|
||||
});
|
||||
// Call a hook in plugins
|
||||
Book.prototype.callHook = function(name, data) {
|
||||
return this.plugins.hook(name, data);
|
||||
};
|
||||
|
||||
module.exports= Book;
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
var path = require('path');
|
||||
|
||||
module.exports = {
|
||||
// Options that can't be extend
|
||||
'configFile': 'book',
|
||||
'generator': 'website',
|
||||
'extension': null,
|
||||
|
||||
// Book metadats (somes are extracted from the README by default)
|
||||
'title': null,
|
||||
'description': null,
|
||||
'isbn': null,
|
||||
'language': 'en',
|
||||
'direction': null,
|
||||
'author': null,
|
||||
|
||||
// version of gitbook to use
|
||||
'gitbook': '*',
|
||||
|
||||
// Structure
|
||||
'structure': {
|
||||
'langs': 'LANGS.md',
|
||||
'readme': 'README.md',
|
||||
'glossary': 'GLOSSARY.md',
|
||||
'summary': 'SUMMARY.md'
|
||||
},
|
||||
|
||||
// CSS Styles
|
||||
'styles': {
|
||||
'website': 'styles/website.css',
|
||||
'print': 'styles/print.css',
|
||||
'ebook': 'styles/ebook.css',
|
||||
'pdf': 'styles/pdf.css',
|
||||
'mobi': 'styles/mobi.css',
|
||||
'epub': 'styles/epub.css'
|
||||
},
|
||||
|
||||
// Plugins list, can contain '-name' for removing default plugins
|
||||
'plugins': [],
|
||||
|
||||
// Global configuration for plugins
|
||||
'pluginsConfig': {},
|
||||
|
||||
// Variables for templating
|
||||
'variables': {},
|
||||
|
||||
// Set another theme with your own layout
|
||||
// It's recommended to use plugins or add more options for default theme, though
|
||||
// See https://github.com/GitbookIO/gitbook/issues/209
|
||||
'theme': path.resolve(__dirname, '../theme'),
|
||||
|
||||
// Links in template (null: default, false: remove, string: new value)
|
||||
'links': {
|
||||
// Custom links at top of sidebar
|
||||
'sidebar': {
|
||||
// 'Custom link name': 'https://customlink.com'
|
||||
},
|
||||
|
||||
// Sharing links
|
||||
'sharing': {
|
||||
'google': null,
|
||||
'facebook': null,
|
||||
'twitter': null,
|
||||
'weibo': null,
|
||||
'all': null
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// Options for PDF generation
|
||||
'pdf': {
|
||||
// Add toc at the end of the file
|
||||
'toc': true,
|
||||
|
||||
// Add page numbers to the bottom of every page
|
||||
'pageNumbers': false,
|
||||
|
||||
// Font for the file content
|
||||
'fontSize': 12,
|
||||
'fontFamily': 'Helvetica',
|
||||
|
||||
// Paper size for the pdf
|
||||
// Choices are [u’a0’, u’a1’, u’a2’, u’a3’, u’a4’, u’a5’, u’a6’, u’b0’, u’b1’, u’b2’, u’b3’, u’b4’, u’b5’, u’b6’, u’legal’, u’letter’]
|
||||
'paperSize': 'a4',
|
||||
|
||||
// How to mark detected chapters.
|
||||
// Choices are “pagebreak”, “rule”, 'both' or “none”.
|
||||
'chapterMark' : 'pagebreak',
|
||||
|
||||
// An XPath expression. Page breaks are inserted before the specified elements.
|
||||
// To disable use the expression: '/'
|
||||
'pageBreaksBefore': '/',
|
||||
|
||||
// Margin (in pts)
|
||||
// Note: 72 pts equals 1 inch
|
||||
'margin': {
|
||||
'right': 62,
|
||||
'left': 62,
|
||||
'top': 56,
|
||||
'bottom': 56
|
||||
},
|
||||
|
||||
// Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
|
||||
'headerTemplate': null,
|
||||
|
||||
// Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
|
||||
'footerTemplate': null
|
||||
}
|
||||
};
|
||||
+109
-169
@@ -1,38 +1,57 @@
|
||||
var _ = require("lodash");
|
||||
var Q = require("q");
|
||||
var path = require("path");
|
||||
var _ = require('lodash');
|
||||
var Q = require('q');
|
||||
var path = require('path');
|
||||
var semver = require('semver');
|
||||
|
||||
var pkg = require('../package.json');
|
||||
var fs = require("./utils/fs");
|
||||
var i18n = require("./utils/i18n");
|
||||
var i18n = require('./utils/i18n');
|
||||
var version = require('./version');
|
||||
|
||||
var DEFAULT_CONFIG = require('./config_default');
|
||||
|
||||
// Default plugins added to each books
|
||||
var defaultsPlugins = [];
|
||||
var DEFAULT_PLUGINS = ['highlight', 'search', 'sharing', 'fontsettings'];
|
||||
|
||||
// Check if a plugin is a default plugin
|
||||
// Plugin should be in the list
|
||||
// And version from book.json specified for this plugin should be satisfied
|
||||
function isDefaultPlugin(name, version) {
|
||||
if (!_.contains(DEFAULT_PLUGINS, name)) return false;
|
||||
|
||||
try {
|
||||
var pluginPkg = require('gitbook-plugin-'+name+'/package.json');
|
||||
return semver.satisfies(pluginPkg.version, version || '*');
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize a list of plugins to use
|
||||
function normalizePluginsList(plugins) {
|
||||
function normalizePluginsList(plugins, addDefaults) {
|
||||
// Normalize list to an array
|
||||
plugins = _.isString(plugins) ? plugins.split(",") : (plugins || []);
|
||||
plugins = _.isString(plugins) ? plugins.split(',') : (plugins || []);
|
||||
|
||||
// Remove empty parts
|
||||
plugins = _.compact(plugins);
|
||||
|
||||
// Divide as {name, version} to handle format like "myplugin@1.0.0"
|
||||
// Divide as {name, version} to handle format like 'myplugin@1.0.0'
|
||||
plugins = _.map(plugins, function(plugin) {
|
||||
if (plugin.name) return plugin;
|
||||
|
||||
var parts = plugin.split("@");
|
||||
var parts = plugin.split('@');
|
||||
var name = parts[0];
|
||||
var version = parts[1];
|
||||
return {
|
||||
'name': parts[0],
|
||||
'version': parts[1] // optional
|
||||
}
|
||||
'name': name,
|
||||
'version': version, // optional
|
||||
'isDefault': isDefaultPlugin(name, version)
|
||||
};
|
||||
});
|
||||
|
||||
// List plugins to remove
|
||||
var toremove = _.chain(plugins)
|
||||
.filter(function(plugin) {
|
||||
return plugin.name.length > 0 && plugin.name[0] == "-";
|
||||
return plugin.name.length > 0 && plugin.name[0] == '-';
|
||||
})
|
||||
.map(function(plugin) {
|
||||
return plugin.name.slice(1);
|
||||
@@ -40,54 +59,33 @@ function normalizePluginsList(plugins) {
|
||||
.value();
|
||||
|
||||
// Merge with defaults
|
||||
plugins = _.chain(plugins)
|
||||
.concat(_.map(defaultsPlugins, function(plugin) {
|
||||
return { 'name': plugin }
|
||||
}))
|
||||
.uniq()
|
||||
.value();
|
||||
if (addDefaults !== false) {
|
||||
_.each(DEFAULT_PLUGINS, function(plugin) {
|
||||
if (_.find(plugins, { name: plugin })) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build final list
|
||||
plugins.push({
|
||||
'name': plugin,
|
||||
'isDefault': true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Remove plugin that start with '-'
|
||||
plugins = _.filter(plugins, function(plugin) {
|
||||
return !_.contains(toremove, plugin.name) && !(plugin.name.length > 0 && plugin.name[0] == "-");
|
||||
return !_.contains(toremove, plugin.name) && !(plugin.name.length > 0 && plugin.name[0] == '-');
|
||||
});
|
||||
|
||||
// Remove duplicates
|
||||
plugins = _.uniq(plugins, 'name');
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
// Normalize a list of plugin name to use
|
||||
function normalizePluginsNames(plugins) {
|
||||
return _.pluck(normalizePluginsList(plugins), "name");
|
||||
};
|
||||
|
||||
var Configuration = function(book, options) {
|
||||
var that = this;
|
||||
|
||||
this.book = book;
|
||||
|
||||
this.options = _.cloneDeep(Configuration.DEFAULT);
|
||||
this.options = _.merge(this.options, options || {});
|
||||
|
||||
// options.input == book.root
|
||||
Object.defineProperty(this.options, "input", {
|
||||
get: function () {
|
||||
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;
|
||||
}
|
||||
});
|
||||
this.replace(options);
|
||||
};
|
||||
|
||||
// Read and parse the configuration
|
||||
@@ -96,11 +94,11 @@ Configuration.prototype.load = function() {
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
var configPath, config;
|
||||
var configPath, _config;
|
||||
|
||||
try {
|
||||
configPath = require.resolve(
|
||||
path.resolve(that.book.root, that.options.configFile)
|
||||
that.book.resolve(that.options.configFile)
|
||||
);
|
||||
|
||||
// Invalidate node.js cache for livreloading
|
||||
@@ -118,18 +116,20 @@ Configuration.prototype.load = function() {
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
if (!semver.satisfies(pkg.version, that.options.gitbook)) {
|
||||
throw "GitBook version doesn't satisfy version required by the book: "+that.options.gitbook;
|
||||
}
|
||||
if (that.options.gitbook == "*") {
|
||||
that.book.log.warn.ln("you should specify a gitbook version to use in your book.json, for example: "+(_.first(pkg.version.split("."))+".x.x"));
|
||||
if (!that.book.isSubBook()) {
|
||||
if (!version.satisfies(that.options.gitbook)) {
|
||||
throw new Error('GitBook version doesn\'t satisfy version required by the book: '+that.options.gitbook);
|
||||
}
|
||||
if (that.options.gitbook != '*' && !semver.satisfies(semver.inc(pkg.version, 'patch'), that.options.gitbook)) {
|
||||
that.book.log.warn.ln('gitbook version specified in your book.json might be too strict for future patches, \''+(_.first(pkg.version.split('.'))+'.x.x')+'\' is more adequate');
|
||||
}
|
||||
}
|
||||
|
||||
that.options.output = path.resolve(that.options.output || path.join(that.book.root, "_book"));
|
||||
that.options.output = path.resolve(that.options.output || that.book.resolve('_book'));
|
||||
that.options.plugins = normalizePluginsList(that.options.plugins);
|
||||
that.options.defaultsPlugins = normalizePluginsList(that.options.defaultsPlugins || "");
|
||||
that.options.defaultsPlugins = normalizePluginsList(that.options.defaultsPlugins || '', false);
|
||||
that.options.plugins = _.union(that.options.plugins, that.options.defaultsPlugins);
|
||||
that.options.plugins = _.uniq(that.options.plugins, "name");
|
||||
that.options.plugins = _.uniq(that.options.plugins, 'name');
|
||||
|
||||
// Default value for text direction (from language)
|
||||
if (!that.options.direction) {
|
||||
@@ -146,9 +146,43 @@ Configuration.prototype.extend = function(options) {
|
||||
_.extend(this.options, options);
|
||||
};
|
||||
|
||||
// Replace the whole configuration
|
||||
Configuration.prototype.replace = function(options) {
|
||||
var that = this;
|
||||
|
||||
this.options = _.cloneDeep(DEFAULT_CONFIG);
|
||||
this.options = _.merge(this.options, options || {});
|
||||
|
||||
// options.input == book.root
|
||||
Object.defineProperty(this.options, 'input', {
|
||||
get: function () {
|
||||
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;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Dump configuration as json object
|
||||
Configuration.prototype.dump = function() {
|
||||
return _.cloneDeep(this.options);
|
||||
};
|
||||
|
||||
// Get structure file
|
||||
Configuration.prototype.getStructure = function(name) {
|
||||
return this.options.structure[name].split(".").slice(0, -1).join(".");
|
||||
return this.options.structure[name].split('.').slice(0, -1).join('.');
|
||||
};
|
||||
|
||||
// Return normalized language
|
||||
@@ -156,111 +190,17 @@ Configuration.prototype.normalizeLanguage = function() {
|
||||
return i18n.normalizeLanguage(this.options.language);
|
||||
};
|
||||
|
||||
|
||||
// Default configuration
|
||||
Configuration.DEFAULT = {
|
||||
// Options that can't be extend
|
||||
"configFile": "book",
|
||||
"generator": "website",
|
||||
"extension": null,
|
||||
|
||||
// Book metadats (somes are extracted from the README by default)
|
||||
"title": null,
|
||||
"description": null,
|
||||
"isbn": null,
|
||||
"language": "en",
|
||||
"direction": null,
|
||||
"author": null,
|
||||
|
||||
// version of gitbook to use
|
||||
"gitbook": "*",
|
||||
|
||||
// Structure
|
||||
"structure": {
|
||||
"langs": "LANGS.md",
|
||||
"readme": "README.md",
|
||||
"glossary": "GLOSSARY.md",
|
||||
"summary": "SUMMARY.md"
|
||||
},
|
||||
|
||||
// CSS Styles
|
||||
"styles": {
|
||||
"website": "styles/website.css",
|
||||
"print": "styles/print.css",
|
||||
"ebook": "styles/ebook.css",
|
||||
"pdf": "styles/pdf.css",
|
||||
"mobi": "styles/mobi.css",
|
||||
"epub": "styles/epub.css"
|
||||
},
|
||||
|
||||
// Plugins list, can contain "-name" for removing default plugins
|
||||
"plugins": [],
|
||||
|
||||
// Global configuration for plugins
|
||||
"pluginsConfig": {
|
||||
"fontSettings": {
|
||||
"theme": null, //"sepia", "night" or "white",
|
||||
"family": "sans",// "serif" or "sans",
|
||||
"size": 2 // 1 - 4
|
||||
}
|
||||
},
|
||||
|
||||
// Variables for templating
|
||||
"variables": {},
|
||||
|
||||
// Set another theme with your own layout
|
||||
// It's recommended to use plugins or add more options for default theme, though
|
||||
// See https://github.com/GitbookIO/gitbook/issues/209
|
||||
"theme": path.resolve(__dirname, '../theme'),
|
||||
|
||||
// Links in template (null: default, false: remove, string: new value)
|
||||
"links": {
|
||||
// Custom links at top of sidebar
|
||||
"sidebar": {
|
||||
//"Custom link name": "https://customlink.com"
|
||||
},
|
||||
|
||||
// Sharing links
|
||||
"sharing": {
|
||||
"google": null,
|
||||
"facebook": null,
|
||||
"twitter": null,
|
||||
"weibo": null,
|
||||
"all": null
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// Options for PDF generation
|
||||
"pdf": {
|
||||
// Add toc at the end of the file
|
||||
"toc": true,
|
||||
|
||||
// Add page numbers to the bottom of every page
|
||||
"pageNumbers": false,
|
||||
|
||||
// Font size for the file content
|
||||
"fontSize": 12,
|
||||
|
||||
// Paper size for the pdf
|
||||
// Choices are [u’a0’, u’a1’, u’a2’, u’a3’, u’a4’, u’a5’, u’a6’, u’b0’, u’b1’, u’b2’, u’b3’, u’b4’, u’b5’, u’b6’, u’legal’, u’letter’]
|
||||
"paperSize": "a4",
|
||||
|
||||
// Margin (in pts)
|
||||
// Note: 72 pts equals 1 inch
|
||||
"margin": {
|
||||
"right": 62,
|
||||
"left": 62,
|
||||
"top": 56,
|
||||
"bottom": 56
|
||||
},
|
||||
|
||||
//Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
|
||||
"headerTemplate": "",
|
||||
|
||||
//Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
|
||||
"footerTemplate": ""
|
||||
}
|
||||
// Return a configuration
|
||||
Configuration.prototype.get = function(key, def) {
|
||||
return _.get(this.options, key, def);
|
||||
};
|
||||
|
||||
// Update a configuration
|
||||
Configuration.prototype.set = function(key, value) {
|
||||
return _.set(this.options, key, value);
|
||||
};
|
||||
|
||||
// Default configuration
|
||||
Configuration.DEFAULT = DEFAULT_CONFIG;
|
||||
|
||||
module.exports= Configuration;
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
var _ = require('lodash');
|
||||
var path = require('path');
|
||||
var nunjucks = require('nunjucks');
|
||||
|
||||
var git = require('./utils/git');
|
||||
var fs = require('./utils/fs');
|
||||
var pathUtil = require('./utils/path');
|
||||
|
||||
// The loader should handle relative and git url
|
||||
var BookLoader = nunjucks.Loader.extend({
|
||||
async: true,
|
||||
|
||||
init: function(book, opts) {
|
||||
this.opts = _.defaults(opts || {}, {
|
||||
interpolate: _.identity
|
||||
});
|
||||
this.book = book;
|
||||
},
|
||||
|
||||
getSource: function(fileurl, callback) {
|
||||
var that = this;
|
||||
|
||||
git.resolveFile(fileurl)
|
||||
.then(function(filepath) {
|
||||
// Is local file
|
||||
if (!filepath) filepath = path.resolve(fileurl);
|
||||
else that.book.log.debug.ln('resolve from git', fileurl, 'to', filepath);
|
||||
|
||||
// Read file from absolute path
|
||||
return fs.readFile(filepath)
|
||||
.then(function(source) {
|
||||
return that.opts.interpolate(filepath, source.toString());
|
||||
})
|
||||
.then(function(source) {
|
||||
return {
|
||||
src: source,
|
||||
path: filepath,
|
||||
|
||||
// We disable cache sincde content is modified (shortcuts, ...)
|
||||
noCache: true
|
||||
};
|
||||
});
|
||||
})
|
||||
.nodeify(callback);
|
||||
},
|
||||
|
||||
resolve: function(from, to) {
|
||||
// If origin is in the book, we enforce result file to be in the book
|
||||
if (this.book.fileIsInBook(from)) {
|
||||
return this.book.resolve(
|
||||
this.book.relative(path.dirname(from)),
|
||||
to
|
||||
);
|
||||
}
|
||||
|
||||
// If origin is in a git repository, we resolve file in the git repository
|
||||
var gitRoot = git.resolveRoot(from);
|
||||
if (gitRoot) {
|
||||
return pathUtil.resolveInRoot(gitRoot, to);
|
||||
}
|
||||
|
||||
// If origin is not in the book (include from a git content ref)
|
||||
return path.resolve(path.dirname(from), to);
|
||||
},
|
||||
|
||||
// Handle all files as relative, so that nunjucks pass responsability to 'resolve'
|
||||
// Only git urls are considered as absolute
|
||||
isRelative: function(filename) {
|
||||
return !git.checkUrl(filename);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = BookLoader;
|
||||
+15
-17
@@ -1,14 +1,12 @@
|
||||
var _ = require("lodash");
|
||||
var path = require("path");
|
||||
var Q = require("q");
|
||||
var fs = require("./utils/fs");
|
||||
|
||||
var Plugin = require("./plugin");
|
||||
var _ = require('lodash');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var fs = require('./utils/fs');
|
||||
|
||||
var BaseGenerator = function(book) {
|
||||
this.book = book;
|
||||
|
||||
Object.defineProperty(this, "options", {
|
||||
Object.defineProperty(this, 'options', {
|
||||
get: function () {
|
||||
return this.book.options;
|
||||
}
|
||||
@@ -18,25 +16,25 @@ var BaseGenerator = function(book) {
|
||||
};
|
||||
|
||||
BaseGenerator.prototype.callHook = function(name, data) {
|
||||
return this.book.plugins.hook(name, data);
|
||||
return this.book.callHook(name, data);
|
||||
};
|
||||
|
||||
// Prepare the genertor
|
||||
BaseGenerator.prototype.prepare = function() {
|
||||
var that = this;
|
||||
|
||||
return that.callHook("init");
|
||||
return that.callHook('init');
|
||||
};
|
||||
|
||||
// Write a parsed file to the output
|
||||
BaseGenerator.prototype.convertFile = function(input) {
|
||||
return Q.reject(new Error("Could not convert "+input));
|
||||
return Q.reject(new Error('Could not convert '+input));
|
||||
};
|
||||
|
||||
// Copy file to the output (non parsable)
|
||||
BaseGenerator.prototype.transferFile = function(input) {
|
||||
return fs.copy(
|
||||
path.join(this.book.root, input),
|
||||
this.book.resolve(input),
|
||||
path.join(this.options.output, input)
|
||||
);
|
||||
};
|
||||
@@ -53,26 +51,26 @@ BaseGenerator.prototype.copyCover = function() {
|
||||
var that = this;
|
||||
|
||||
return Q.all([
|
||||
fs.copy(path.join(that.book.root, "cover.jpg"), path.join(that.options.output, "cover.jpg")),
|
||||
fs.copy(path.join(that.book.root, "cover_small.jpg"), path.join(that.options.output, "cover_small.jpg"))
|
||||
fs.copy(that.book.resolve('cover.jpg'), path.join(that.options.output, 'cover.jpg')),
|
||||
fs.copy(that.book.resolve('cover_small.jpg'), path.join(that.options.output, 'cover_small.jpg'))
|
||||
])
|
||||
.fail(function() {
|
||||
// If orignaly from multi-lang, try copy from parent
|
||||
if (!that.book.isSubBook()) return;
|
||||
|
||||
return Q.all([
|
||||
fs.copy(path.join(that.book.parentRoot(), "cover.jpg"), path.join(that.options.output, "cover.jpg")),
|
||||
fs.copy(path.join(that.book.parentRoot(), "cover_small.jpg"), path.join(that.options.output, "cover_small.jpg"))
|
||||
fs.copy(path.join(that.book.parentRoot(), 'cover.jpg'), path.join(that.options.output, 'cover.jpg')),
|
||||
fs.copy(path.join(that.book.parentRoot(), 'cover_small.jpg'), path.join(that.options.output, 'cover_small.jpg'))
|
||||
]);
|
||||
})
|
||||
.fail(function(err) {
|
||||
.fail(function() {
|
||||
return Q();
|
||||
});
|
||||
};
|
||||
|
||||
// At teh end of the generation
|
||||
BaseGenerator.prototype.finish = function() {
|
||||
return Q.reject(new Error("Could not finish generation"));
|
||||
return Q.reject(new Error('Could not finish generation'));
|
||||
};
|
||||
|
||||
module.exports = BaseGenerator;
|
||||
|
||||
+89
-56
@@ -1,12 +1,13 @@
|
||||
var util = require("util");
|
||||
var path = require("path");
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
var juice = require('juice');
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
var fs = require("../utils/fs");
|
||||
var stringUtils = require("../utils/string");
|
||||
var BaseGenerator = require("./website");
|
||||
var fs = require('../utils/fs');
|
||||
var stringUtils = require('../utils/string');
|
||||
var BaseGenerator = require('./website');
|
||||
|
||||
var Generator = function(book, format) {
|
||||
BaseGenerator.apply(this, arguments);
|
||||
@@ -15,10 +16,10 @@ var Generator = function(book, format) {
|
||||
this.ebookFormat = format;
|
||||
|
||||
// Resources namespace
|
||||
this.namespace = "ebook";
|
||||
this.namespace = 'ebook';
|
||||
|
||||
// Styles to use
|
||||
this.styles = _.compact(["print", "ebook", this.ebookFormat]);
|
||||
this.styles = _.compact(['print', 'ebook', this.ebookFormat]);
|
||||
|
||||
// Convert images (svg -> png)
|
||||
this.convertImages = true;
|
||||
@@ -26,9 +27,9 @@ var Generator = function(book, format) {
|
||||
util.inherits(Generator, BaseGenerator);
|
||||
|
||||
Generator.prototype.prepareTemplates = function() {
|
||||
this.templates["page"] = this.book.plugins.template("ebook:page") || path.resolve(this.options.theme, 'templates/ebook/page.html');
|
||||
this.templates["summary"] = this.book.plugins.template("ebook:summary") || path.resolve(this.options.theme, 'templates/ebook/summary.html');
|
||||
this.templates["glossary"] = this.book.plugins.template("ebook:glossary") || path.resolve(this.options.theme, 'templates/ebook/glossary.html');
|
||||
this.templates.page = this.book.plugins.template('ebook:page') || path.resolve(this.options.theme, 'templates/ebook/page.html');
|
||||
this.templates.summary = this.book.plugins.template('ebook:summary') || path.resolve(this.options.theme, 'templates/ebook/summary.html');
|
||||
this.templates.glossary = this.book.plugins.template('ebook:glossary') || path.resolve(this.options.theme, 'templates/ebook/glossary.html');
|
||||
|
||||
return Q();
|
||||
};
|
||||
@@ -37,8 +38,39 @@ Generator.prototype.prepareTemplates = function() {
|
||||
Generator.prototype.writeSummary = function() {
|
||||
var that = this;
|
||||
|
||||
that.book.log.info.ln("write SUMMARY.html");
|
||||
return this._writeTemplate(this.templates["summary"], {}, path.join(this.options.output, "SUMMARY.html"));
|
||||
that.book.log.info.ln('write SUMMARY.html');
|
||||
return this._writeTemplate(this.templates.summary, {}, path.join(this.options.output, 'SUMMARY.html'));
|
||||
};
|
||||
|
||||
// Return template for footer/header with inlined css
|
||||
Generator.prototype.getPDFTemplate = function(id) {
|
||||
var tpl = this.options.pdf[id+'Template'];
|
||||
var defaultTpl = path.resolve(this.options.theme, 'templates/ebook/'+id+'.html');
|
||||
var defaultCSS = path.resolve(this.options.theme, 'assets/ebook/pdf.css');
|
||||
|
||||
// Default template from theme
|
||||
if (!tpl && fs.existsSync(defaultTpl)) {
|
||||
tpl = fs.readFileSync(defaultTpl, { encoding: 'utf-8' });
|
||||
}
|
||||
|
||||
// Inline CSS using juice
|
||||
var stylesheets = [];
|
||||
|
||||
// From theme
|
||||
if (fs.existsSync(defaultCSS)) {
|
||||
stylesheets.push(fs.readFileSync(defaultCSS, { encoding: 'utf-8' }));
|
||||
}
|
||||
|
||||
// Custom PDF style
|
||||
if (this.styles.pdf) {
|
||||
stylesheets.push(fs.readFileSync(this.book.resolve(this.styles.pdf), { encoding: 'utf-8' }));
|
||||
}
|
||||
|
||||
tpl = juice(tpl, {
|
||||
extraCss: stylesheets.concat('/n')
|
||||
});
|
||||
|
||||
return tpl;
|
||||
};
|
||||
|
||||
Generator.prototype.finish = function() {
|
||||
@@ -52,70 +84,71 @@ Generator.prototype.finish = function() {
|
||||
.then(function() {
|
||||
if (!that.ebookFormat) return Q();
|
||||
|
||||
var d = Q.defer();
|
||||
|
||||
if (!that.options.cover && fs.existsSync(path.join(that.options.output, "cover.jpg"))) {
|
||||
that.options.cover = path.join(that.options.output, "cover.jpg");
|
||||
if (!that.options.cover && fs.existsSync(path.join(that.options.output, 'cover.jpg'))) {
|
||||
that.options.cover = path.join(that.options.output, 'cover.jpg');
|
||||
}
|
||||
|
||||
var d = Q.defer();
|
||||
|
||||
var _options = {
|
||||
"--cover": that.options.cover,
|
||||
"--title": that.options.title,
|
||||
"--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",
|
||||
"--page-breaks-before": "/",
|
||||
"--level1-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-1 ')]",
|
||||
"--level2-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-2 ')]",
|
||||
"--level3-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-3 ')]",
|
||||
"--no-chapters-in-toc": true,
|
||||
"--max-levels": "1",
|
||||
"--breadth-first": true
|
||||
'--cover': that.options.cover,
|
||||
'--title': that.options.title,
|
||||
'--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 \')]',
|
||||
'--level1-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-1 \')]',
|
||||
'--level2-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-2 \')]',
|
||||
'--level3-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-3 \')]',
|
||||
'--no-chapters-in-toc': true,
|
||||
'--max-levels': '1',
|
||||
'--breadth-first': true
|
||||
};
|
||||
|
||||
if (that.ebookFormat == "pdf") {
|
||||
if (that.ebookFormat == 'pdf') {
|
||||
var pdfOptions = that.options.pdf;
|
||||
|
||||
_.extend(_options, {
|
||||
"--margin-left": String(pdfOptions.margin.left),
|
||||
"--margin-right": String(pdfOptions.margin.right),
|
||||
"--margin-top": String(pdfOptions.margin.top),
|
||||
"--margin-bottom": String(pdfOptions.margin.bottom),
|
||||
"--pdf-default-font-size": String(pdfOptions.fontSize),
|
||||
"--pdf-mono-font-size": String(pdfOptions.fontSize),
|
||||
"--paper-size": String(pdfOptions.paperSize),
|
||||
"--pdf-page-numbers": Boolean(pdfOptions.pageNumbers),
|
||||
"--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>"
|
||||
'--chapter-mark': String(pdfOptions.chapterMark),
|
||||
'--page-breaks-before': String(pdfOptions.pageBreaksBefore),
|
||||
'--margin-left': String(pdfOptions.margin.left),
|
||||
'--margin-right': String(pdfOptions.margin.right),
|
||||
'--margin-top': String(pdfOptions.margin.top),
|
||||
'--margin-bottom': String(pdfOptions.margin.bottom),
|
||||
'--pdf-default-font-size': String(pdfOptions.fontSize),
|
||||
'--pdf-mono-font-size': String(pdfOptions.fontSize),
|
||||
'--paper-size': String(pdfOptions.paperSize),
|
||||
'--pdf-page-numbers': Boolean(pdfOptions.pageNumbers),
|
||||
'--pdf-header-template': that.getPDFTemplate('header'),
|
||||
'--pdf-footer-template': that.getPDFTemplate('footer'),
|
||||
'--pdf-sans-family': String(pdfOptions.fontFamily)
|
||||
});
|
||||
} else if (that.ebookFormat == "epub") {
|
||||
} else if (that.ebookFormat == 'epub') {
|
||||
_.extend(_options, {
|
||||
"--dont-split-on-page-breaks": true
|
||||
'--dont-split-on-page-breaks': true
|
||||
});
|
||||
}
|
||||
|
||||
var command = [
|
||||
"ebook-convert",
|
||||
path.join(that.options.output, "SUMMARY.html"),
|
||||
path.join(that.options.output, "index."+that.ebookFormat),
|
||||
'ebook-convert',
|
||||
path.join(that.options.output, 'SUMMARY.html'),
|
||||
path.join(that.options.output, 'index.'+that.ebookFormat),
|
||||
stringUtils.optionsToShellArgs(_options)
|
||||
].join(" ");
|
||||
].join(' ');
|
||||
|
||||
that.book.log.info("start conversion to", that.ebookFormat, "....");
|
||||
that.book.log.info('start conversion to', that.ebookFormat, '....');
|
||||
|
||||
var child = exec(command, function (error, stdout, stderr) {
|
||||
var child = exec(command, function (error, stdout) {
|
||||
if (error) {
|
||||
that.book.log.info.fail();
|
||||
|
||||
if (error.code == 127) {
|
||||
error.message = "Need to install ebook-convert from Calibre";
|
||||
error.message = 'Need to install ebook-convert from Calibre';
|
||||
} else {
|
||||
error.message = error.message + " "+stdout;
|
||||
error.message = error.message + ' '+stdout;
|
||||
}
|
||||
return d.reject(error);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ var Generator = function() {
|
||||
util.inherits(Generator, BaseGenerator);
|
||||
|
||||
// Ignore some methods
|
||||
Generator.prototype.transferFile = function(input) { };
|
||||
Generator.prototype.transferFile = function() { };
|
||||
|
||||
// Convert an input file
|
||||
Generator.prototype.convertFile = function(input) {
|
||||
@@ -44,7 +44,7 @@ Generator.prototype.finish = function() {
|
||||
// Write README.json
|
||||
Generator.prototype.writeReadme = function() {
|
||||
var that = this;
|
||||
var mainlang, langs;
|
||||
var mainLang, langs, readme;
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
|
||||
+40
-54
@@ -1,19 +1,18 @@
|
||||
var util = require("util");
|
||||
var path = require("path");
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
|
||||
var nunjucks = require("nunjucks");
|
||||
var AutoEscapeExtension = require("nunjucks-autoescape")(nunjucks);
|
||||
var FilterExtension = require("nunjucks-filter")(nunjucks);
|
||||
var nunjucks = require('nunjucks');
|
||||
var AutoEscapeExtension = require('nunjucks-autoescape')(nunjucks);
|
||||
var FilterExtension = require('nunjucks-filter')(nunjucks);
|
||||
|
||||
var fs = require("../utils/fs");
|
||||
var BaseGenerator = require("../generator");
|
||||
var links = require("../utils/links");
|
||||
var pageUtil = require("../utils/page");
|
||||
var i18n = require("../utils/i18n");
|
||||
var fs = require('../utils/fs');
|
||||
var BaseGenerator = require('../generator');
|
||||
var links = require('../utils/links');
|
||||
var i18n = require('../utils/i18n');
|
||||
|
||||
var pkg = require("../../package.json");
|
||||
var pkg = require('../../package.json');
|
||||
|
||||
var Generator = function() {
|
||||
BaseGenerator.apply(this, arguments);
|
||||
@@ -22,10 +21,10 @@ var Generator = function() {
|
||||
this.revision = new Date();
|
||||
|
||||
// Resources namespace
|
||||
this.namespace = "website";
|
||||
this.namespace = 'website';
|
||||
|
||||
// Style to integrates in the output
|
||||
this.styles = ["website"];
|
||||
this.styles = ['website'];
|
||||
|
||||
// Convert images (svg -> png)
|
||||
this.convertImages = false;
|
||||
@@ -50,7 +49,7 @@ Generator.prototype.prepareStyles = function() {
|
||||
this.styles = _.chain(this.styles)
|
||||
.map(function(style) {
|
||||
var stylePath = that.options.styles[style];
|
||||
if (stylePath && fs.existsSync(path.resolve(that.book.root, stylePath))) {
|
||||
if (stylePath && fs.existsSync(that.book.resolve(stylePath))) {
|
||||
return [style, stylePath];
|
||||
}
|
||||
return null;
|
||||
@@ -64,9 +63,9 @@ Generator.prototype.prepareStyles = function() {
|
||||
|
||||
// Prepare templates
|
||||
Generator.prototype.prepareTemplates = function() {
|
||||
this.templates["page"] = this.book.plugins.template("site:page") || path.resolve(this.options.theme, 'templates/website/page.html');
|
||||
this.templates["langs"] = this.book.plugins.template("site:langs") || path.resolve(this.options.theme, 'templates/website/langs.html');
|
||||
this.templates["glossary"] = this.book.plugins.template("site:glossary") || path.resolve(this.options.theme, 'templates/website/glossary.html');
|
||||
this.templates.page = this.book.plugins.template('site:page') || path.resolve(this.options.theme, 'templates/website/page.html');
|
||||
this.templates.langs = this.book.plugins.template('site:langs') || path.resolve(this.options.theme, 'templates/website/langs.html');
|
||||
this.templates.glossary = this.book.plugins.template('site:glossary') || path.resolve(this.options.theme, 'templates/website/glossary.html');
|
||||
|
||||
return Q();
|
||||
};
|
||||
@@ -80,7 +79,7 @@ Generator.prototype.prepareTemplateEngine = function() {
|
||||
var language = that.book.config.normalizeLanguage();
|
||||
|
||||
if (!i18n.hasLocale(language)) {
|
||||
that.book.log.warn.ln("Language '"+language+"' is not available as a layout locales (en, "+i18n.getLocales().join(", ")+")");
|
||||
that.book.log.warn.ln('Language "'+language+'" is not available as a layout locales (en, '+i18n.getLocales().join(', ')+')');
|
||||
}
|
||||
|
||||
var folders = _.chain(that.templates)
|
||||
@@ -97,9 +96,9 @@ Generator.prototype.prepareTemplateEngine = function() {
|
||||
);
|
||||
|
||||
// Add filter
|
||||
that.env.addFilter("contentLink", that.book.contentLink.bind(that.book));
|
||||
that.env.addFilter('contentLink', that.book.contentLink.bind(that.book));
|
||||
that.env.addFilter('lvl', function(lvl) {
|
||||
return lvl.split(".").length;
|
||||
return lvl.split('.').length;
|
||||
});
|
||||
|
||||
// Add extension
|
||||
@@ -113,8 +112,7 @@ Generator.prototype.finish = function() {
|
||||
return this.copyAssets()
|
||||
.then(this.copyCover)
|
||||
.then(this.writeGlossary)
|
||||
.then(this.writeSearchIndex)
|
||||
.then(this.writeLangsIndex)
|
||||
.then(this.writeLangsIndex);
|
||||
};
|
||||
|
||||
// Convert an input file
|
||||
@@ -124,60 +122,48 @@ Generator.prototype.convertFile = function(input) {
|
||||
return that.book.parsePage(input, {
|
||||
convertImages: that.convertImages,
|
||||
interpolateTemplate: function(page) {
|
||||
return that.callHook("page:before", page);
|
||||
return that.callHook('page:before', page);
|
||||
},
|
||||
interpolateContent: function(page) {
|
||||
return that.callHook("page", page);
|
||||
return that.callHook('page', page);
|
||||
}
|
||||
})
|
||||
.then(function(page) {
|
||||
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) || ".";
|
||||
var basePath = path.relative(path.dirname(output), that.options.output) || '.';
|
||||
if (process.platform === 'win32') basePath = basePath.replace(/\\/g, '/');
|
||||
|
||||
that.book.log.debug.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"], {
|
||||
return that._writeTemplate(that.templates.page, {
|
||||
progress: page.progress,
|
||||
|
||||
_input: page.path,
|
||||
content: page.sections,
|
||||
|
||||
basePath: basePath,
|
||||
staticBase: links.join(basePath, "gitbook")
|
||||
staticBase: links.join(basePath, 'gitbook')
|
||||
}, output);
|
||||
});
|
||||
};
|
||||
|
||||
// Write the index for langs
|
||||
Generator.prototype.writeLangsIndex = function() {
|
||||
var that = this;
|
||||
if (!this.book.langs.length) return Q();
|
||||
return this._writeTemplate(this.templates["langs"], {
|
||||
|
||||
return this._writeTemplate(this.templates.langs, {
|
||||
langs: this.book.langs
|
||||
}, path.join(this.options.output, "index.html"));
|
||||
}, path.join(this.options.output, 'index.html'));
|
||||
};
|
||||
|
||||
// Write glossary
|
||||
Generator.prototype.writeGlossary = function() {
|
||||
var that = this;
|
||||
|
||||
// No glossary
|
||||
if (this.book.glossary.length == 0) return Q();
|
||||
if (this.book.glossary.length === 0) return Q();
|
||||
|
||||
return this._writeTemplate(this.templates["glossary"], {}, path.join(this.options.output, "GLOSSARY.html"));
|
||||
};
|
||||
|
||||
// Write the search index
|
||||
Generator.prototype.writeSearchIndex = function() {
|
||||
var that = this;
|
||||
|
||||
return fs.writeFile(
|
||||
path.join(this.options.output, "search_index.json"),
|
||||
JSON.stringify(this.book.searchIndex)
|
||||
);
|
||||
return this._writeTemplate(this.templates.glossary, {}, path.join(this.options.output, 'GLOSSARY.html'));
|
||||
};
|
||||
|
||||
// Convert a page into a normalized data set
|
||||
@@ -194,7 +180,7 @@ Generator.prototype.normalizePage = function(page) {
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
return _callHook("page");
|
||||
return _callHook('page');
|
||||
})
|
||||
.then(function() {
|
||||
return page;
|
||||
@@ -207,7 +193,7 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
|
||||
|
||||
interpolate = interpolate || _.identity;
|
||||
return Q()
|
||||
.then(function(sections) {
|
||||
.then(function() {
|
||||
return that.env.render(
|
||||
tpl,
|
||||
_.extend({
|
||||
@@ -236,8 +222,8 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
|
||||
|
||||
options: that.options,
|
||||
|
||||
basePath: ".",
|
||||
staticBase: path.join(".", "gitbook"),
|
||||
basePath: '.',
|
||||
staticBase: path.join('.', 'gitbook'),
|
||||
|
||||
'__': that.book.i18n.bind(that.book)
|
||||
}, options)
|
||||
@@ -258,15 +244,15 @@ Generator.prototype.copyAssets = function() {
|
||||
|
||||
// Copy gitbook assets
|
||||
return fs.copy(
|
||||
path.join(that.options.theme, "assets"),
|
||||
path.join(that.options.output, "gitbook")
|
||||
path.join(that.options.theme, 'assets/'+this.namespace),
|
||||
path.join(that.options.output, 'gitbook')
|
||||
)
|
||||
|
||||
// Copy plugins assets
|
||||
.then(function() {
|
||||
return Q.all(
|
||||
_.map(that.book.plugins.list, function(plugin) {
|
||||
var pluginAssets = path.join(that.options.output, "gitbook/plugins/", plugin.name);
|
||||
var pluginAssets = path.join(that.options.output, 'gitbook/plugins/', plugin.name);
|
||||
return plugin.copyAssets(pluginAssets, that.namespace);
|
||||
})
|
||||
);
|
||||
|
||||
+57
-52
@@ -1,45 +1,50 @@
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var path = require("path");
|
||||
/*eslint no-console: 0*/
|
||||
|
||||
var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
var path = require('path');
|
||||
var tinylr = require('tiny-lr');
|
||||
var color = require('bash-color');
|
||||
|
||||
var Book = require("./book");
|
||||
var Plugin = require("./plugin");
|
||||
var Server = require("./utils/server");
|
||||
var stringUtils = require("./utils/string");
|
||||
var watch = require("./utils/watch");
|
||||
var logger = require("./utils/logger");
|
||||
var Book = require('./book');
|
||||
var initBook = require('./init');
|
||||
var Server = require('./utils/server');
|
||||
var stringUtils = require('./utils/string');
|
||||
var watch = require('./utils/watch');
|
||||
var logger = require('./utils/logger');
|
||||
|
||||
var LOG_OPTION = {
|
||||
name: "log",
|
||||
description: "Minimum log level to display",
|
||||
name: 'log',
|
||||
description: 'Minimum log level to display',
|
||||
values: _.chain(logger.LEVELS).keys().map(stringUtils.toLowerCase).value(),
|
||||
defaults: "info"
|
||||
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'
|
||||
};
|
||||
|
||||
// Export init to gitbook library
|
||||
Book.init = initBook;
|
||||
|
||||
module.exports = {
|
||||
Book: Book,
|
||||
LOG_LEVELS: logger.LEVELS,
|
||||
|
||||
commands: _.flatten([
|
||||
{
|
||||
name: "build [book] [output]",
|
||||
description: "build a book",
|
||||
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 output = args[1] || path.join(input, '_book');
|
||||
|
||||
var book = new Book(input, _.extend({}, {
|
||||
'config': {
|
||||
@@ -53,16 +58,16 @@ module.exports = {
|
||||
return book.generate(kwargs.format);
|
||||
})
|
||||
.then(function(){
|
||||
console.log("");
|
||||
console.log(color.green("Done, without error"));
|
||||
console.log('');
|
||||
console.log(color.green('Done, without error'));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_.map(["pdf", "epub", "mobi"], function(ebookType) {
|
||||
_.map(['pdf', 'epub', 'mobi'], function(ebookType) {
|
||||
return {
|
||||
name: ebookType+" [book] [output]",
|
||||
description: "build a book to "+ebookType,
|
||||
name: ebookType+' [book] [output]',
|
||||
description: 'build a book to '+ebookType,
|
||||
options: [
|
||||
LOG_OPTION
|
||||
],
|
||||
@@ -81,30 +86,30 @@ module.exports = {
|
||||
});
|
||||
})
|
||||
.then(function(){
|
||||
console.log("");
|
||||
console.log(color.green("Done, without error"));
|
||||
console.log('');
|
||||
console.log(color.green('Done, without error'));
|
||||
});
|
||||
}
|
||||
};
|
||||
}),
|
||||
|
||||
{
|
||||
name: "serve [book]",
|
||||
description: "Build then serve a gitbook from a directory",
|
||||
name: 'serve [book]',
|
||||
description: 'Build then serve a gitbook from a directory',
|
||||
options: [
|
||||
{
|
||||
name: "port",
|
||||
description: "Port for server to listen on",
|
||||
name: 'port',
|
||||
description: 'Port for server to listen on',
|
||||
defaults: 4000
|
||||
},
|
||||
{
|
||||
name: "lrport",
|
||||
description: "Port for livereload server to listen on",
|
||||
name: 'lrport',
|
||||
description: 'Port for livereload server to listen on',
|
||||
defaults: 35729
|
||||
},
|
||||
{
|
||||
name: "watch",
|
||||
description: "Enable/disable file watcher",
|
||||
name: 'watch',
|
||||
description: 'Enable/disable file watcher',
|
||||
defaults: true
|
||||
},
|
||||
FORMAT_OPTION,
|
||||
@@ -116,16 +121,16 @@ module.exports = {
|
||||
|
||||
// Init livereload server
|
||||
var lrServer = tinylr({});
|
||||
var lrPath = undefined;
|
||||
var lrPath;
|
||||
|
||||
var generate = function() {
|
||||
if (server.isRunning()) console.log("Stopping server");
|
||||
if (server.isRunning()) console.log('Stopping server');
|
||||
|
||||
return server.stop()
|
||||
.then(function() {
|
||||
var book = new Book(input, _.extend({}, {
|
||||
'config': {
|
||||
'defaultsPlugins': ["livereload"]
|
||||
'defaultsPlugins': ['livereload']
|
||||
},
|
||||
'logLevel': kwargs.log
|
||||
}));
|
||||
@@ -158,11 +163,11 @@ module.exports = {
|
||||
.then(function(filepath) {
|
||||
// set livereload path
|
||||
lrPath = filepath;
|
||||
console.log("Restart after change in files");
|
||||
console.log('Restart after change in file', filepath);
|
||||
console.log('');
|
||||
return generate();
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -170,16 +175,16 @@ module.exports = {
|
||||
.then(function() {
|
||||
console.log('Live reload server started on port:', kwargs.lrport);
|
||||
console.log('Press CTRL+C to quit ...');
|
||||
console.log('')
|
||||
console.log('');
|
||||
return generate();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: "install [book]",
|
||||
description: "install plugins dependencies",
|
||||
exec: function(args, kwargs) {
|
||||
name: 'install [book]',
|
||||
description: 'install plugins dependencies',
|
||||
exec: function(args) {
|
||||
var input = args[0] || process.cwd();
|
||||
|
||||
var book = new Book(input);
|
||||
@@ -189,20 +194,20 @@ module.exports = {
|
||||
return book.plugins.install();
|
||||
})
|
||||
.then(function(){
|
||||
console.log("");
|
||||
console.log(color.green("Done, without error"));
|
||||
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())
|
||||
name: 'init [directory]',
|
||||
description: 'create files and folders based on contents of SUMMARY.md',
|
||||
exec: function(args) {
|
||||
return initBook(args[0] || process.cwd())
|
||||
.then(function(){
|
||||
console.log("");
|
||||
console.log(color.green("Done, without error"));
|
||||
console.log('');
|
||||
console.log(color.green('Done, without error'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
var _ = require('lodash');
|
||||
var Q = require('q');
|
||||
var path = require('path');
|
||||
|
||||
var Book = require('./book');
|
||||
var fs = require('./utils/fs');
|
||||
|
||||
// Initialize folder structure for a book
|
||||
// Read SUMMARY to created the right chapter
|
||||
function initBook(root, opts) {
|
||||
var book = new Book(root, opts);
|
||||
var extensionToUse = '.md';
|
||||
|
||||
var chaptersPaths = function(chapters) {
|
||||
return _.reduce(chapters || [], function(accu, chapter) {
|
||||
var o = {
|
||||
title: chapter.title
|
||||
};
|
||||
if (chapter.path) o.path = chapter.path;
|
||||
|
||||
return accu.concat(
|
||||
[o].concat(chaptersPaths(chapter.articles))
|
||||
);
|
||||
}, []);
|
||||
};
|
||||
|
||||
book.log.info.ln('init book at', root);
|
||||
return fs.mkdirp(root)
|
||||
.then(function() {
|
||||
book.log.info.ln('detect structure from SUMMARY (if it exists)');
|
||||
return book.parseSummary();
|
||||
})
|
||||
.fail(function() {
|
||||
return Q();
|
||||
})
|
||||
.then(function() {
|
||||
var summary = book.summaryFile || 'SUMMARY.md';
|
||||
var chapters = book.summary.chapters || [];
|
||||
extensionToUse = path.extname(summary);
|
||||
|
||||
if (chapters.length === 0) {
|
||||
chapters = [
|
||||
{
|
||||
title: 'Summary',
|
||||
path: 'SUMMARY'+extensionToUse
|
||||
},
|
||||
{
|
||||
title: 'Introduction',
|
||||
path: 'README'+extensionToUse
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
return Q(chaptersPaths(chapters));
|
||||
})
|
||||
.then(function(chapters) {
|
||||
// Create files that don't exist
|
||||
return Q.all(_.map(chapters, function(chapter) {
|
||||
if (!chapter.path) return Q();
|
||||
var absolutePath = path.resolve(book.root, chapter.path);
|
||||
|
||||
return fs.exists(absolutePath)
|
||||
.then(function(exists) {
|
||||
if(exists) {
|
||||
book.log.info.ln('found', chapter.path);
|
||||
return;
|
||||
} else {
|
||||
book.log.info.ln('create', chapter.path);
|
||||
}
|
||||
|
||||
return fs.mkdirp(path.dirname(absolutePath))
|
||||
.then(function() {
|
||||
return fs.writeFile(absolutePath, '# '+chapter.title+'\n');
|
||||
});
|
||||
});
|
||||
}));
|
||||
})
|
||||
.then(function() {
|
||||
book.log.info.ln('initialization is finished');
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = initBook;
|
||||
+82
-24
@@ -1,16 +1,27 @@
|
||||
var _ = require("lodash");
|
||||
var Q = require("q");
|
||||
var semver = require("semver");
|
||||
var path = require("path");
|
||||
var url = require("url");
|
||||
var fs = require("./utils/fs");
|
||||
var _ = require('lodash');
|
||||
var Q = require('q');
|
||||
var path = require('path');
|
||||
var url = require('url');
|
||||
var fs = require('./utils/fs');
|
||||
var resolve = require('resolve');
|
||||
var mergeDefaults = require('merge-defaults');
|
||||
var jsonschema = require('jsonschema');
|
||||
var jsonSchemaDefaults = require('json-schema-defaults');
|
||||
|
||||
var version = require('./version');
|
||||
|
||||
var PLUGIN_PREFIX = 'gitbook-plugin-';
|
||||
|
||||
// Return an absolute name for the plugin (the one on NPM)
|
||||
function absoluteName(name) {
|
||||
if (name.indexOf(PLUGIN_PREFIX) === 0) return name;
|
||||
return [PLUGIN_PREFIX, name].join('');
|
||||
}
|
||||
|
||||
var pkg = require("../package.json");
|
||||
|
||||
var Plugin = function(book, name) {
|
||||
this.book = book;
|
||||
this.name = name;
|
||||
this.name = absoluteName(name);
|
||||
this.packageInfos = {};
|
||||
this.infos = {};
|
||||
|
||||
@@ -18,9 +29,8 @@ var Plugin = function(book, name) {
|
||||
_.bindAll(this);
|
||||
|
||||
_.each([
|
||||
"gitbook-plugin-"+name,
|
||||
"gitbook-"+name,
|
||||
name,
|
||||
absoluteName(name),
|
||||
name
|
||||
], function(_name) {
|
||||
// Load from the book
|
||||
if (this.load(_name, book.root)) return false;
|
||||
@@ -31,20 +41,27 @@ var Plugin = function(book, name) {
|
||||
};
|
||||
|
||||
// Type of plugins resources
|
||||
Plugin.RESOURCES = ["js", "css"];
|
||||
Plugin.RESOURCES = ['js', 'css'];
|
||||
Plugin.HOOKS = [
|
||||
"init", "finish", "finish:before"
|
||||
]
|
||||
'init', 'finish', 'finish:before', 'config', 'page', 'page:before'
|
||||
];
|
||||
|
||||
// Return the reduce name for the plugin
|
||||
// "gitbook-plugin-test" -> "test"
|
||||
// Return a relative name for the plugin (the one on GitBook)
|
||||
Plugin.prototype.reducedName = function() {
|
||||
return this.name.replace(PLUGIN_PREFIX, '');
|
||||
};
|
||||
|
||||
// Load from a name
|
||||
Plugin.prototype.load = function(name, baseDir) {
|
||||
try {
|
||||
var res = resolve.sync(name+"/package.json", { basedir: baseDir });
|
||||
var res = resolve.sync(name+'/package.json', { basedir: baseDir });
|
||||
|
||||
this.baseDir = path.dirname(res);
|
||||
this.packageInfos = require(res);
|
||||
this.infos = require(resolve.sync(name, { basedir: baseDir }));
|
||||
this.name = name;
|
||||
this.name = this.packageInfos.name;
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
@@ -62,13 +79,13 @@ Plugin.prototype.normalizeResource = function(resource) {
|
||||
// so we will simply link to using it's URL
|
||||
if (parsed.protocol) {
|
||||
return {
|
||||
"url": resource
|
||||
'url': resource
|
||||
};
|
||||
}
|
||||
|
||||
// This will be copied over from disk
|
||||
// and shipped with the book's build
|
||||
return { "path": this.name+"/"+resource };
|
||||
return { 'path': this.name+'/'+resource };
|
||||
};
|
||||
|
||||
// Return resources
|
||||
@@ -77,7 +94,7 @@ Plugin.prototype._getResources = function(base) {
|
||||
var book = this.infos[base];
|
||||
|
||||
// Compatibility with version 1.x.x
|
||||
if (base == "website") book = book || this.infos["book"];
|
||||
if (base == 'website') book = book || this.infos.book;
|
||||
|
||||
// Nothing specified, fallback to default
|
||||
if (!book) {
|
||||
@@ -85,7 +102,7 @@ Plugin.prototype._getResources = function(base) {
|
||||
}
|
||||
|
||||
// Dynamic function
|
||||
if(typeof book === "function") {
|
||||
if(typeof book === 'function') {
|
||||
// Call giving it the context of our book
|
||||
return Q().then(book.bind(this.book));
|
||||
}
|
||||
@@ -127,18 +144,49 @@ Plugin.prototype.isValid = function() {
|
||||
this.packageInfos.name &&
|
||||
this.packageInfos.engines &&
|
||||
this.packageInfos.engines.gitbook &&
|
||||
semver.satisfies(pkg.version, this.packageInfos.engines.gitbook)
|
||||
version.satisfies(this.packageInfos.engines.gitbook)
|
||||
);
|
||||
|
||||
// Valid hooks
|
||||
_.each(this.infos.hooks, function(hook, hookName) {
|
||||
if (_.contains(Plugin.HOOKS, hookName)) return;
|
||||
that.book.log.warn.ln("Hook '"+hookName+" 'used by plugin '"+that.packageInfos.name+"' has been removed or is deprecated");
|
||||
that.book.log.warn.ln('Hook "'+hookName+'"" used by plugin "'+that.packageInfos.name+'" has been removed or is deprecated');
|
||||
});
|
||||
|
||||
return isValid;
|
||||
};
|
||||
|
||||
// Normalize, validate configuration for this plugin using its schema
|
||||
// Throw an error when shcema is not respected
|
||||
Plugin.prototype.validateConfig = function(config) {
|
||||
var that = this;
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
var schema = that.packageInfos.gitbook || {};
|
||||
if (!schema) return config;
|
||||
|
||||
// Normalize schema
|
||||
schema.id = '/pluginsConfig.'+that.reducedName();
|
||||
schema.type = 'object';
|
||||
|
||||
// Validate and throw if invalid
|
||||
var v = new jsonschema.Validator();
|
||||
var result = v.validate(config, schema, {
|
||||
propertyName: 'pluginsConfig.'+that.reducedName()
|
||||
});
|
||||
|
||||
// Throw error
|
||||
if (result.errors.length > 0) {
|
||||
throw new Error('Configuration Error: '+result.errors[0].stack);
|
||||
}
|
||||
|
||||
// Insert default values
|
||||
var defaults = jsonSchemaDefaults(schema);
|
||||
return mergeDefaults(config, defaults);
|
||||
});
|
||||
};
|
||||
|
||||
// Resolve file path
|
||||
Plugin.prototype.resolveFile = function(filename) {
|
||||
return path.resolve(this.baseDir, filename);
|
||||
@@ -154,8 +202,8 @@ 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 removed in the coming versions");
|
||||
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 removed in the coming versions');
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
@@ -180,4 +228,14 @@ Plugin.prototype.copyAssets = function(out, base) {
|
||||
}, _.constant(false));
|
||||
};
|
||||
|
||||
// Get config from book
|
||||
Plugin.prototype.getConfig = function() {
|
||||
return this.book.config.get('pluginsConfig.'+this.reducedName(), {});
|
||||
};
|
||||
|
||||
// Set configuration for this plugin
|
||||
Plugin.prototype.setConfig = function(values) {
|
||||
return this.book.config.set('pluginsConfig.'+this.reducedName(), values);
|
||||
};
|
||||
|
||||
module.exports = Plugin;
|
||||
|
||||
+57
-52
@@ -1,11 +1,11 @@
|
||||
var _ = require("lodash");
|
||||
var Q = require("q");
|
||||
var _ = require('lodash');
|
||||
var Q = require('q');
|
||||
var npmi = require('npmi');
|
||||
var npm = require('npm');
|
||||
var semver = require('semver');
|
||||
|
||||
var Plugin = require("./plugin");
|
||||
var pkg = require("../package.json");
|
||||
var Plugin = require('./plugin');
|
||||
var version = require('./version');
|
||||
|
||||
var initNPM = _.memoize(function() {
|
||||
return Q.nfcall(npm.load, { silent: true, loglevel: 'silent' });
|
||||
@@ -23,7 +23,7 @@ var PluginsList = function(book, plugins) {
|
||||
this.failed = [];
|
||||
|
||||
// Namespaces
|
||||
this.namespaces = _.chain(["website", "ebook"])
|
||||
this.namespaces = _.chain(['website', 'ebook'])
|
||||
.map(function(namespace) {
|
||||
return [
|
||||
namespace,
|
||||
@@ -53,7 +53,7 @@ PluginsList.prototype.count = function() {
|
||||
};
|
||||
|
||||
// Add and load a plugin
|
||||
PluginsList.prototype.load = function(plugin, options) {
|
||||
PluginsList.prototype.load = function(plugin) {
|
||||
var that = this;
|
||||
|
||||
if (_.isArray(plugin)) {
|
||||
@@ -66,7 +66,7 @@ PluginsList.prototype.load = function(plugin, options) {
|
||||
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, "....");
|
||||
that.log.info('load plugin', plugin.name, '....');
|
||||
if (!plugin.isValid()) {
|
||||
that.log.info.fail();
|
||||
that.failed.push(plugin.name);
|
||||
@@ -78,38 +78,46 @@ PluginsList.prototype.load = function(plugin, options) {
|
||||
that.list.push(plugin);
|
||||
}
|
||||
|
||||
// Extract filters
|
||||
_.each(plugin.getFilters(), function(filterFunc, filterName) {
|
||||
that.book.template.addFilter(filterName, filterFunc);
|
||||
});
|
||||
return Q()
|
||||
|
||||
// Extract blocks
|
||||
_.each(plugin.getBlocks(), function(block, blockName) {
|
||||
that.book.template.addBlock(blockName, block);
|
||||
});
|
||||
// Validate and normalize configuration
|
||||
.then(function() {
|
||||
var config = plugin.getConfig();
|
||||
return plugin.validateConfig(config);
|
||||
})
|
||||
.then(function(config) {
|
||||
// Update configuration
|
||||
plugin.setConfig(config);
|
||||
|
||||
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 filters
|
||||
that.book.template.addFilters(plugin.getFilters());
|
||||
|
||||
// Extract js and css
|
||||
_.each(Plugin.RESOURCES, function(resourceType) {
|
||||
namespace.resources[resourceType] = (namespace.resources[resourceType] || []).concat(plResources[resourceType] || []);
|
||||
// Extract blocks
|
||||
that.book.template.addBlocks(plugin.getBlocks());
|
||||
|
||||
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] || []);
|
||||
});
|
||||
|
||||
// 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);
|
||||
});
|
||||
});
|
||||
|
||||
// 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());
|
||||
});
|
||||
}, Q());
|
||||
});
|
||||
};
|
||||
|
||||
// Call a hook
|
||||
@@ -139,7 +147,7 @@ PluginsList.prototype.html = function(namespace, tag, context, options) {
|
||||
var htmlSnippets = this.namespaces[namespace].html[tag];
|
||||
return _.map(htmlSnippets || [], function(code) {
|
||||
return code.call(context, options);
|
||||
}).join("\n");
|
||||
}).join('\n');
|
||||
};
|
||||
|
||||
// Return a resources map for a namespace
|
||||
@@ -150,18 +158,17 @@ PluginsList.prototype.resources = function(namespace) {
|
||||
// Install plugins from a book
|
||||
PluginsList.prototype.install = function() {
|
||||
var that = this;
|
||||
var defaultsPlugins = _.pluck(that.book.options.defaultsPlugins)
|
||||
|
||||
// Remove defaults (no need to install)
|
||||
var plugins = _.filter(that.book.options.plugins, function(plugin) {
|
||||
return !_.contains(defaultsPlugins, plugin.name);
|
||||
var plugins = _.reject(that.book.options.plugins, {
|
||||
isDefault: true
|
||||
});
|
||||
|
||||
// Install plugins one by one
|
||||
that.book.log.info.ln(plugins.length+" plugins to install");
|
||||
that.book.log.info.ln(plugins.length+' plugins to install');
|
||||
return _.reduce(plugins, function(prev, plugin) {
|
||||
return prev.then(function() {
|
||||
var fullname = "gitbook-plugin-"+plugin.name;
|
||||
var fullname = 'gitbook-plugin-'+plugin.name;
|
||||
|
||||
return Q()
|
||||
|
||||
@@ -169,10 +176,10 @@ PluginsList.prototype.install = function() {
|
||||
.then(function() {
|
||||
if (plugin.version) return plugin.version;
|
||||
|
||||
that.book.log.info.ln("No version specified, resolve plugin", plugin.name);
|
||||
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);
|
||||
return Q.nfcall(npm.commands.view, [fullname+'@*', 'engines'], true);
|
||||
})
|
||||
.then(function(versions) {
|
||||
return _.chain(versions)
|
||||
@@ -180,16 +187,16 @@ PluginsList.prototype.install = function() {
|
||||
.map(function(v) {
|
||||
return {
|
||||
version: v[0],
|
||||
gitbook: (v[1].engines || {})["gitbook"]
|
||||
}
|
||||
gitbook: (v[1].engines || {}).gitbook
|
||||
};
|
||||
})
|
||||
.filter(function(v) {
|
||||
return v.gitbook && semver.satisfies(pkg.version, v.gitbook);
|
||||
return v.gitbook && version.satisfies(v.gitbook);
|
||||
})
|
||||
.sort(function(v1, v2) {
|
||||
return semver.lt(v1.version, v2.version)? 1 : -1;
|
||||
})
|
||||
.pluck("version")
|
||||
.pluck('version')
|
||||
.first()
|
||||
.value();
|
||||
});
|
||||
@@ -198,10 +205,10 @@ PluginsList.prototype.install = function() {
|
||||
// Install the plugin with the resolved version
|
||||
.then(function(version) {
|
||||
if (!version) {
|
||||
throw "Found no satisfactory version for plugin "+plugin.name;
|
||||
throw 'Found no satisfactory version for plugin '+plugin.name;
|
||||
}
|
||||
|
||||
that.book.log.info.ln("install plugin", plugin.name, "from npm ("+fullname+") with version", version);
|
||||
that.book.log.info.ln('install plugin', plugin.name, 'from npm ('+fullname+') with version', version);
|
||||
return Q.nfcall(npmi, {
|
||||
'name': fullname,
|
||||
'version': version,
|
||||
@@ -214,12 +221,10 @@ PluginsList.prototype.install = function() {
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
that.book.log.info.ok("plugin", plugin.name, "installed with success");
|
||||
that.book.log.info.ok('plugin', plugin.name, 'installed with success');
|
||||
});
|
||||
});
|
||||
}, Q());
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports = PluginsList;
|
||||
|
||||
+160
-94
@@ -1,57 +1,42 @@
|
||||
var _ = require("lodash");
|
||||
var Q = require("q");
|
||||
var path = require("path");
|
||||
var nunjucks = require("nunjucks");
|
||||
var escapeStringRegexp = require("escape-string-regexp");
|
||||
var _ = require('lodash');
|
||||
var Q = require('q');
|
||||
var path = require('path');
|
||||
var nunjucks = require('nunjucks');
|
||||
var parsers = require('gitbook-parsers');
|
||||
var escapeStringRegexp = require('escape-string-regexp');
|
||||
|
||||
var git = require("./utils/git");
|
||||
var fs = require("./utils/fs");
|
||||
var batch = require("./utils/batch");
|
||||
var pkg = require("../package.json");
|
||||
var batch = require('./utils/batch');
|
||||
var pkg = require('../package.json');
|
||||
var defaultBlocks = require('./blocks');
|
||||
var BookLoader = require('./conrefs_loader');
|
||||
|
||||
|
||||
// The loader should handle relative and git url
|
||||
var BookLoader = nunjucks.Loader.extend({
|
||||
async: true,
|
||||
|
||||
init: function(book) {
|
||||
this.book = book;
|
||||
},
|
||||
|
||||
getSource: function(fileurl, callback) {
|
||||
var that = this;
|
||||
|
||||
git.resolveFile(fileurl)
|
||||
.then(function(filepath) {
|
||||
// Is local file
|
||||
if (!filepath) filepath = path.resolve(that.book.root, fileurl);
|
||||
else that.book.log.debug.ln("resolve from git", fileurl, "to", filepath)
|
||||
|
||||
// Read file from absolute path
|
||||
return fs.readFile(filepath)
|
||||
.then(function(source) {
|
||||
return {
|
||||
src: source.toString(),
|
||||
path: filepath
|
||||
}
|
||||
});
|
||||
})
|
||||
.nodeify(callback);
|
||||
},
|
||||
|
||||
resolve: function(from, to) {
|
||||
return path.resolve(path.dirname(from), to);
|
||||
}
|
||||
});
|
||||
// Normalize result from a block
|
||||
function normBlockResult(blk) {
|
||||
if (_.isString(blk)) blk = { body: blk };
|
||||
return blk;
|
||||
}
|
||||
|
||||
|
||||
var TemplateEngine = function(book) {
|
||||
var that = this;
|
||||
|
||||
this.book = book;
|
||||
this.log = this.book.log;
|
||||
|
||||
// Template loader
|
||||
this.loader = new BookLoader(book, {
|
||||
// Replace shortcuts in imported files
|
||||
interpolate: function(filepath, source) {
|
||||
var parser = parsers.get(path.extname(filepath));
|
||||
var type = parser? parser.name : null;
|
||||
|
||||
return that.applyShortcuts(type, source);
|
||||
}
|
||||
});
|
||||
|
||||
// Nunjucks env
|
||||
this.env = new nunjucks.Environment(
|
||||
new BookLoader(book),
|
||||
this.loader,
|
||||
{
|
||||
// Escaping is done after by the markdown parser
|
||||
autoescape: false,
|
||||
@@ -71,49 +56,48 @@ var TemplateEngine = function(book) {
|
||||
// List of tags shortcuts
|
||||
this.shortcuts = [];
|
||||
|
||||
// Map of blocks
|
||||
// Map of blocks bodies (that requires post-processing)
|
||||
this.blockBodies = {};
|
||||
|
||||
// Map of added blocks
|
||||
this.blocks = {};
|
||||
|
||||
// Bind methods
|
||||
_.bindAll(this);
|
||||
|
||||
// Default block "html" that return html not parsed
|
||||
this.addBlock("html", {
|
||||
process: _.identity
|
||||
});
|
||||
// Add default blocks
|
||||
this.addBlocks(defaultBlocks);
|
||||
};
|
||||
|
||||
// Process a block in a context
|
||||
// Process the result of block in a context
|
||||
TemplateEngine.prototype.processBlock = function(blk) {
|
||||
if (_.isString(blk)) blk = { body: blk };
|
||||
|
||||
blk = _.defaults(blk, {
|
||||
parse: false,
|
||||
post: undefined
|
||||
});
|
||||
blk.id = _.uniqueId("blk");
|
||||
blk.id = _.uniqueId('blk');
|
||||
|
||||
var toAdd = (!blk.parse) || (blk.post != undefined);
|
||||
var toAdd = (!blk.parse) || (blk.post !== undefined);
|
||||
|
||||
// Add to global map
|
||||
if (toAdd) this.blocks[blk.id] = blk;
|
||||
if (toAdd) this.blockBodies[blk.id] = blk;
|
||||
|
||||
//Parsable block, just return it
|
||||
// Parsable block, just return it
|
||||
if (blk.parse) {
|
||||
return blk.body;
|
||||
}
|
||||
|
||||
// Return it as a macro
|
||||
return "%+%"+blk.id+"%+%";
|
||||
// Return it as a position marker
|
||||
return '@%@'+blk.id+'@%@';
|
||||
};
|
||||
|
||||
// Replace blocks by body after processing
|
||||
// This is done to avoid that markdown processer parse the block content
|
||||
// Replace position markers of blocks by body after processing
|
||||
// This is done to avoid that markdown/asciidoc processer parse the block content
|
||||
TemplateEngine.prototype.replaceBlocks = function(content) {
|
||||
var that = this;
|
||||
|
||||
return content.replace(/\%\+\%([\s\S]+?)\%\+\%/g, function(match, key) {
|
||||
var blk = that.blocks[key];
|
||||
return content.replace(/\@\%\@([\s\S]+?)\@\%\@/g, function(match, key) {
|
||||
var blk = that.blockBodies[key];
|
||||
if (!blk) return match;
|
||||
|
||||
var body = blk.body;
|
||||
@@ -141,11 +125,13 @@ TemplateEngine.prototype.bindContext = function(func) {
|
||||
TemplateEngine.prototype.addFilter = function(filterName, func) {
|
||||
try {
|
||||
this.env.getFilter(filterName);
|
||||
this.log.warn.ln("conflict in filters, '"+filterName+"' is already set");
|
||||
this.log.warn.ln('conflict in filters, \''+filterName+'\' is already set');
|
||||
return false;
|
||||
} catch(e) {}
|
||||
} catch(e) {
|
||||
// Filter doesn't exist
|
||||
}
|
||||
|
||||
this.log.debug.ln("add filter '"+filterName+"'");
|
||||
this.log.debug.ln('add filter \''+filterName+'\'');
|
||||
this.env.addFilter(filterName, this.bindContext(function() {
|
||||
var ctx = this;
|
||||
var args = Array.prototype.slice.apply(arguments);
|
||||
@@ -160,29 +146,65 @@ TemplateEngine.prototype.addFilter = function(filterName, func) {
|
||||
return true;
|
||||
};
|
||||
|
||||
// Add multiple filters
|
||||
TemplateEngine.prototype.addFilters = function(filters) {
|
||||
_.each(filters, function(filter, name) {
|
||||
this.addFilter(name, filter);
|
||||
}, this);
|
||||
};
|
||||
|
||||
// Return nunjucks extension name of a block
|
||||
TemplateEngine.prototype.blockExtName = function(name) {
|
||||
return 'Block'+name+'Extension';
|
||||
};
|
||||
|
||||
// Test if a block is defined
|
||||
TemplateEngine.prototype.hasBlock = function(name) {
|
||||
return this.env.hasExtension(this.blockExtName(name));
|
||||
};
|
||||
|
||||
// Remove a block
|
||||
TemplateEngine.prototype.removeBlock = function(name) {
|
||||
if (!this.hasBlock(name)) return;
|
||||
|
||||
// Remove nunjucks extension
|
||||
this.env.removeExtension(this.blockExtName(name));
|
||||
|
||||
// Cleanup shortcuts
|
||||
this.shortcuts = _.reject(this.shortcuts, {
|
||||
block: name
|
||||
});
|
||||
};
|
||||
|
||||
// Add a block
|
||||
TemplateEngine.prototype.addBlock = function(name, block) {
|
||||
var that = this;
|
||||
var that = this, Ext, extName;
|
||||
|
||||
if (_.isFunction(block)) block = { process: block };
|
||||
|
||||
block = _.defaults(block || {}, {
|
||||
shortcuts: [],
|
||||
end: "end"+name,
|
||||
end: 'end'+name,
|
||||
process: _.identity,
|
||||
blocks: []
|
||||
});
|
||||
|
||||
var extName = 'Block'+name+'Extension';
|
||||
if (this.env.getExtension(extName)) {
|
||||
this.log.warn.ln("conflict in blocks, '"+name+"' is already defined");
|
||||
return false;
|
||||
extName = this.blockExtName(name);
|
||||
|
||||
if (this.hasBlock(name) && !defaultBlocks[name]) {
|
||||
this.log.warn.ln('conflict in blocks, \''+name+'\' is already defined');
|
||||
}
|
||||
|
||||
this.log.debug.ln("add block '"+name+"'");
|
||||
// Cleanup previous block
|
||||
this.removeBlock(name);
|
||||
|
||||
var Ext = function () {
|
||||
this.log.debug.ln('add block \''+name+'\'');
|
||||
this.blocks[name] = block;
|
||||
|
||||
Ext = function () {
|
||||
this.tags = [name];
|
||||
|
||||
this.parse = function(parser, nodes, lexer) {
|
||||
this.parse = function(parser, nodes) {
|
||||
var body = null;
|
||||
var lastBlockName = null;
|
||||
var lastBlockArgs = null;
|
||||
@@ -223,7 +245,7 @@ TemplateEngine.prototype.addBlock = function(name, block) {
|
||||
var bodies = [body];
|
||||
_.each(block.blocks, function(blockName) {
|
||||
subbodies[blockName] = subbodies[blockName] || [];
|
||||
if (subbodies[blockName].length == 0) {
|
||||
if (subbodies[blockName].length === 0) {
|
||||
subbodies[blockName].push({
|
||||
args: new nodes.NodeList(),
|
||||
body: new nodes.NodeList()
|
||||
@@ -250,7 +272,7 @@ TemplateEngine.prototype.addBlock = function(name, block) {
|
||||
|
||||
// Extract main body and kwargs
|
||||
var body = args.pop();
|
||||
var kwargs = args.pop() || {};
|
||||
var kwargs = _.isObject(_.last(args))? args.pop() : {};
|
||||
|
||||
// Extract blocks body
|
||||
var _blocks = _.map(block.blocks, function(blockName, i){
|
||||
@@ -260,33 +282,31 @@ TemplateEngine.prototype.addBlock = function(name, block) {
|
||||
};
|
||||
});
|
||||
|
||||
var func = that.bindContext(block.process);
|
||||
|
||||
Q()
|
||||
.then(function() {
|
||||
return func.call(context, {
|
||||
return that.applyBlock(name, {
|
||||
body: body(),
|
||||
args: args,
|
||||
kwargs: kwargs,
|
||||
blocks: _blocks
|
||||
});
|
||||
}, context);
|
||||
})
|
||||
|
||||
// process the block returned
|
||||
.then(that.processBlock)
|
||||
.nodeify(callback)
|
||||
.nodeify(callback);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// Add the Extension
|
||||
this.env.addExtension(extName, new Ext());
|
||||
|
||||
// Add shortcuts
|
||||
if (!_.isArray(block.shortcuts)) block.shortcuts = [block.shortcuts];
|
||||
_.each(block.shortcuts, function(shortcut) {
|
||||
this.log.debug.ln("add template shortcut from '"+shortcut.start+"' to block '"+name+"' for parsers ", shortcut.parsers);
|
||||
this.log.debug.ln('add template shortcut from \''+shortcut.start+'\' to block \''+name+'\' for parsers ', shortcut.parsers);
|
||||
this.shortcuts.push({
|
||||
block: name,
|
||||
parsers: shortcut.parsers,
|
||||
start: shortcut.start,
|
||||
end: shortcut.end,
|
||||
@@ -298,24 +318,66 @@ TemplateEngine.prototype.addBlock = function(name, block) {
|
||||
}, this);
|
||||
};
|
||||
|
||||
// Add multiple blocks
|
||||
TemplateEngine.prototype.addBlocks = function(blocks) {
|
||||
_.each(blocks, function(block, name) {
|
||||
this.addBlock(name, block);
|
||||
}, this);
|
||||
};
|
||||
|
||||
// Apply a block to some content
|
||||
// This method result depends on the type of block (async or sync)
|
||||
TemplateEngine.prototype.applyBlock = function(name, blk, ctx) {
|
||||
var func, block, r;
|
||||
|
||||
block = this.blocks[name];
|
||||
if (!block) throw new Error('Block not found \''+name+'\'');
|
||||
if (_.isString(blk)) {
|
||||
blk = {
|
||||
body: blk
|
||||
};
|
||||
}
|
||||
|
||||
blk = _.defaults(blk, {
|
||||
args: [],
|
||||
kwargs: {},
|
||||
blocks: []
|
||||
});
|
||||
|
||||
// Bind and call block processor
|
||||
func = this.bindContext(block.process);
|
||||
r = func.call(ctx || {}, blk);
|
||||
|
||||
if (Q.isPromise(r)) return r.then(normBlockResult);
|
||||
else return normBlockResult(r);
|
||||
};
|
||||
|
||||
// Apply a shortcut to a string
|
||||
TemplateEngine.prototype._applyShortcut = function(parser, content, shortcut) {
|
||||
if (!_.contains(shortcut.parsers, parser)) return content;
|
||||
var regex = new RegExp(
|
||||
escapeStringRegexp(shortcut.start) + "([\\s\\S]*?[^\\$])" + escapeStringRegexp(shortcut.end),
|
||||
escapeStringRegexp(shortcut.start) + '([\\s\\S]*?[^\\$])' + escapeStringRegexp(shortcut.end),
|
||||
'g'
|
||||
);
|
||||
return content.replace(regex, function(all, match) {
|
||||
return "{% "+shortcut.tag.start+" %}"+ match + "{% "+shortcut.tag.end+" %}";
|
||||
return '{% '+shortcut.tag.start+' %}'+ match + '{% '+shortcut.tag.end+' %}';
|
||||
});
|
||||
};
|
||||
|
||||
// Apply all shortcuts to some template string
|
||||
TemplateEngine.prototype.applyShortcuts = function(type, content) {
|
||||
return _.reduce(this.shortcuts, _.partial(this._applyShortcut.bind(this), type), content);
|
||||
};
|
||||
|
||||
// Render a string from the book
|
||||
TemplateEngine.prototype.renderString = function(content, context, options) {
|
||||
var context = _.extend({}, context, {
|
||||
context = _.extend({}, context, {
|
||||
// Variables from book.json
|
||||
book: this.book.options.variables,
|
||||
|
||||
// Complete book.json
|
||||
config: this.book.options,
|
||||
|
||||
// infos about gitbook
|
||||
gitbook: {
|
||||
version: pkg.version,
|
||||
@@ -327,22 +389,26 @@ TemplateEngine.prototype.renderString = function(content, context, options) {
|
||||
type: null
|
||||
});
|
||||
if (options.path) options.path = this.book.resolve(options.path);
|
||||
if (!options.type && options.path) {
|
||||
var parser = parsers.get(path.extname(options.path));
|
||||
options.type = parser? parser.name : null;
|
||||
}
|
||||
|
||||
// Replace shortcuts
|
||||
content = _.reduce(this.shortcuts, _.partial(this._applyShortcut.bind(this), options.type), content);
|
||||
content = this.applyShortcuts(options.type, content);
|
||||
|
||||
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: /, "");
|
||||
err.message = err.message.replace(/^Error: /, '');
|
||||
|
||||
throw err;
|
||||
});
|
||||
};
|
||||
|
||||
// Render a file from the book
|
||||
TemplateEngine.prototype.renderFile = function(filename, options) {
|
||||
var that = this, context;
|
||||
TemplateEngine.prototype.renderFile = function(filename) {
|
||||
var that = this;
|
||||
|
||||
return that.book.readFile(filename)
|
||||
.then(function(content) {
|
||||
@@ -354,11 +420,11 @@ TemplateEngine.prototype.renderFile = function(filename, options) {
|
||||
|
||||
// Render a page from the book
|
||||
TemplateEngine.prototype.renderPage = function(page) {
|
||||
var that = this, context;
|
||||
var that = this;
|
||||
|
||||
return that.book.statFile(page.path)
|
||||
.then(function(stat) {
|
||||
context = {
|
||||
var context = {
|
||||
// infos about the file
|
||||
file: {
|
||||
path: page.path,
|
||||
@@ -380,7 +446,7 @@ TemplateEngine.prototype.postProcess = function(content) {
|
||||
return Q(content)
|
||||
.then(that.replaceBlocks)
|
||||
.then(function(_content) {
|
||||
return batch.execEach(that.blocks, {
|
||||
return batch.execEach(that.blockBodies, {
|
||||
max: 20,
|
||||
fn: function(blk, blkId) {
|
||||
return Q()
|
||||
@@ -389,7 +455,7 @@ TemplateEngine.prototype.postProcess = function(content) {
|
||||
return blk.post();
|
||||
})
|
||||
.then(function() {
|
||||
delete that.blocks[blkId];
|
||||
delete that.blockBodies[blkId];
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
+4
-4
@@ -3,12 +3,12 @@ var _ = require("lodash");
|
||||
|
||||
// Execute a method for all element
|
||||
function execEach(items, options) {
|
||||
if (_.size(items) == 0) return Q();
|
||||
if (_.size(items) === 0) return Q();
|
||||
var concurrents = 0, d = Q.defer(), pending = [];
|
||||
|
||||
options = _.defaults(options || {}, {
|
||||
max: 100,
|
||||
fn: function(item) {}
|
||||
fn: function() {}
|
||||
});
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ function execEach(items, options) {
|
||||
// Next pending
|
||||
var next = pending.shift();
|
||||
|
||||
if (concurrents == 0 && !next) {
|
||||
if (concurrents === 0 && !next) {
|
||||
d.resolve();
|
||||
} else if (next) {
|
||||
startItem.apply(null, next);
|
||||
@@ -38,7 +38,7 @@ function execEach(items, options) {
|
||||
.fail(function(err) {
|
||||
pending = [];
|
||||
d.reject(err);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
_.each(items, startItem);
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
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
|
||||
};
|
||||
+19
-16
@@ -1,18 +1,18 @@
|
||||
var _ = require("lodash");
|
||||
var Q = require("q");
|
||||
var tmp = require("tmp");
|
||||
var path = require("path");
|
||||
var fs = require("graceful-fs");
|
||||
var fsExtra = require("fs-extra");
|
||||
var Ignore = require("fstream-ignore");
|
||||
var _ = require('lodash');
|
||||
var Q = require('q');
|
||||
var tmp = require('tmp');
|
||||
var path = require('path');
|
||||
var fs = require('graceful-fs');
|
||||
var fsExtra = require('fs-extra');
|
||||
var Ignore = require('fstream-ignore');
|
||||
|
||||
var fsUtils = {
|
||||
tmp: {
|
||||
file: function(opt) {
|
||||
return Q.nfcall(tmp.file.bind(tmp), opt).get(0)
|
||||
return Q.nfcall(tmp.file.bind(tmp), opt).get(0);
|
||||
},
|
||||
dir: function() {
|
||||
return Q.nfcall(tmp.dir.bind(tmp)).get(0)
|
||||
return Q.nfcall(tmp.dir.bind(tmp)).get(0);
|
||||
}
|
||||
},
|
||||
list: listFiles,
|
||||
@@ -33,15 +33,15 @@ var fsUtils = {
|
||||
existsSync: fs.existsSync.bind(fs),
|
||||
readFileSync: fs.readFileSync.bind(fs),
|
||||
clean: cleanFolder,
|
||||
getUniqueFilename: getUniqueFilename,
|
||||
}
|
||||
getUniqueFilename: getUniqueFilename
|
||||
};
|
||||
|
||||
// Write a file
|
||||
function writeFile(filename, data, options) {
|
||||
var d = Q.defer();
|
||||
|
||||
try {
|
||||
fs.writeFileSync(filename, data, options)
|
||||
fs.writeFileSync(filename, data, options);
|
||||
} catch(err) {
|
||||
d.reject(err);
|
||||
}
|
||||
@@ -64,6 +64,10 @@ function writeStream(filename, st) {
|
||||
d.reject(err);
|
||||
});
|
||||
|
||||
st.on('error', function(err) {
|
||||
d.reject(err);
|
||||
});
|
||||
|
||||
st.pipe(wstream);
|
||||
|
||||
return d.promise;
|
||||
@@ -73,7 +77,7 @@ function writeStream(filename, st) {
|
||||
function getUniqueFilename(base, filename) {
|
||||
if (!filename) {
|
||||
filename = base;
|
||||
base = "/";
|
||||
base = '/';
|
||||
}
|
||||
|
||||
filename = path.resolve(base, filename);
|
||||
@@ -83,9 +87,8 @@ function getUniqueFilename(base, filename) {
|
||||
var _filename = filename+ext;
|
||||
|
||||
var i = 0;
|
||||
while (1) {
|
||||
if (!fs.existsSync(filename)) break;
|
||||
_filename = filename+"_"+i+ext;
|
||||
while (fs.existsSync(filename)) {
|
||||
_filename = filename+'_'+i+ext;
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
|
||||
+21
-6
@@ -1,11 +1,10 @@
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var url = require("url");
|
||||
var tmp = require("tmp");
|
||||
var path = require("path");
|
||||
var crc = require("crc");
|
||||
var exec = Q.denodeify(require("child_process").exec);
|
||||
var URI = require("URIjs");
|
||||
var pathUtil = require("./path");
|
||||
|
||||
var fs = require("./fs");
|
||||
|
||||
@@ -83,13 +82,12 @@ function cloneGitRepo(host, ref) {
|
||||
return exec("git clone "+host+" "+repoPath)
|
||||
.then(function() {
|
||||
return exec("git checkout "+ref, { cwd: repoPath });
|
||||
})
|
||||
});
|
||||
})
|
||||
.thenResolve(repoPath);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Get file from a git repo
|
||||
function resolveFileFromGit(giturl) {
|
||||
if (_.isString(giturl)) giturl = parseGitUrl(giturl);
|
||||
@@ -102,11 +100,28 @@ function resolveFileFromGit(giturl) {
|
||||
// Resolve relative path
|
||||
return path.resolve(repo, giturl.filepath);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Return root of git repo from a filepath
|
||||
function resolveGitRoot(filepath) {
|
||||
var relativeToGit, repoId;
|
||||
|
||||
// No git repo cloned, or file is not in a git repository
|
||||
if (!GIT_TMP || !pathUtil.isInRoot(GIT_TMP, filepath)) return null;
|
||||
|
||||
// Extract first directory (is the repo id)
|
||||
relativeToGit = path.relative(GIT_TMP, filepath);
|
||||
repoId = _.first(relativeToGit.split(path.sep));
|
||||
if (!repoId) return;
|
||||
|
||||
// Return an absolute file
|
||||
return path.resolve(GIT_TMP, repoId);
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
checkUrl: checkGitUrl,
|
||||
parseUrl: parseGitUrl,
|
||||
resolveFile: resolveFileFromGit
|
||||
resolveFile: resolveFileFromGit,
|
||||
resolveRoot: resolveGitRoot
|
||||
};
|
||||
|
||||
+11
-11
@@ -4,7 +4,7 @@ var fs = require("fs");
|
||||
|
||||
var i18n = require("i18n");
|
||||
|
||||
var I18N_PATH = path.resolve(__dirname, "../../theme/i18n/")
|
||||
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");
|
||||
@@ -17,7 +17,7 @@ i18n.configure({
|
||||
updateFiles: false
|
||||
});
|
||||
|
||||
var compareLocales = function(lang, locale) {
|
||||
function compareLocales(lang, locale) {
|
||||
var langMain = _.first(lang.split("-"));
|
||||
var langSecond = _.last(lang.split("-"));
|
||||
|
||||
@@ -28,7 +28,7 @@ var compareLocales = function(lang, locale) {
|
||||
if (localeMain == langMain) return 50;
|
||||
if (localeSecond == langSecond) return 20;
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
var normalizeLanguage = _.memoize(function(lang) {
|
||||
var language = _.chain(LOCALES)
|
||||
@@ -37,7 +37,7 @@ var normalizeLanguage = _.memoize(function(lang) {
|
||||
return {
|
||||
locale: locale,
|
||||
score: compareLocales(lang, locale)
|
||||
}
|
||||
};
|
||||
})
|
||||
.filter(function(lang) {
|
||||
return lang.score > 0;
|
||||
@@ -49,25 +49,25 @@ var normalizeLanguage = _.memoize(function(lang) {
|
||||
return language || lang;
|
||||
});
|
||||
|
||||
var translate = function(locale, phrase) {
|
||||
function translate(locale, phrase) {
|
||||
var args = Array.prototype.slice.call(arguments, 2);
|
||||
|
||||
return i18n.__.apply({}, [{
|
||||
locale: locale,
|
||||
phrase: phrase
|
||||
}].concat(args));
|
||||
};
|
||||
}
|
||||
|
||||
var getCatalog = function(locale) {
|
||||
function getCatalog(locale) {
|
||||
locale = normalizeLanguage(locale);
|
||||
return i18n.getCatalog(locale);
|
||||
};
|
||||
}
|
||||
|
||||
var getLocales = function() {
|
||||
function getLocales() {
|
||||
return LOCALES;
|
||||
};
|
||||
}
|
||||
|
||||
var hasLocale = function(locale) {
|
||||
function hasLocale(locale) {
|
||||
return _.contains(LOCALES, locale);
|
||||
}
|
||||
|
||||
|
||||
+5
-7
@@ -1,9 +1,7 @@
|
||||
var _ = require("lodash");
|
||||
var Q = require("q");
|
||||
var fs = require("./fs");
|
||||
var spawn = require('spawn-cmd').spawn;
|
||||
|
||||
var links = require("./links");
|
||||
var spawn = require("spawn-cmd").spawn;
|
||||
|
||||
// Convert a svg file
|
||||
var convertSVG = function(source, dest, options) {
|
||||
@@ -14,16 +12,16 @@ var convertSVG = function(source, dest, options) {
|
||||
|
||||
});
|
||||
|
||||
//var command = shellescape(['svgexport', source, dest]);
|
||||
var child = spawn('svgexport', [source, dest]);
|
||||
//var command = shellescape(["svgexport", source, dest]);
|
||||
var child = spawn("svgexport", [source, dest]);
|
||||
|
||||
child.on("error", function(error) {
|
||||
if (error.code == "ENOENT") error = new Error("Need to install 'svgexport' using 'npm install svgexport -g'");
|
||||
if (error.code == "ENOENT") error = new Error("Need to install \"svgexport\" using \"npm install svgexport -g\"");
|
||||
return d.reject(error);
|
||||
});
|
||||
|
||||
child.on("close", function(code) {
|
||||
if (code == 0 && fs.existsSync(dest)) {
|
||||
if (code === 0 && fs.existsSync(dest)) {
|
||||
d.resolve();
|
||||
} else {
|
||||
d.reject(new Error("Error converting "+source+" into "+dest));
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
var url = require('url');
|
||||
var path = require('path');
|
||||
var url = require("url");
|
||||
var path = require("path");
|
||||
|
||||
// Is the link an external link
|
||||
var isExternal = function(href) {
|
||||
@@ -33,7 +33,7 @@ var isAnchor = function(href) {
|
||||
|
||||
// Normalize a path to be a link
|
||||
var normalizeLink = function(s) {
|
||||
return s.replace(/\\/g, '/');
|
||||
return s.replace(/\\/g, "/");
|
||||
};
|
||||
|
||||
// Relative to absolute path
|
||||
@@ -43,7 +43,7 @@ var normalizeLink = function(s) {
|
||||
var toAbsolute = function(_href, dir, outdir) {
|
||||
if (isExternal(_href)) return _href;
|
||||
|
||||
// Path '_href' inside the base folder
|
||||
// Path "_href" inside the base folder
|
||||
var hrefInRoot = path.normalize(path.join(dir, _href));
|
||||
if (_href[0] == "/") hrefInRoot = path.normalize(_href.slice(1));
|
||||
|
||||
|
||||
+9
-9
@@ -1,6 +1,6 @@
|
||||
var _ = require('lodash');
|
||||
var util = require('util');
|
||||
var color = require('bash-color');
|
||||
var _ = require("lodash");
|
||||
var util = require("util");
|
||||
var color = require("bash-color");
|
||||
|
||||
var LEVELS = {
|
||||
DEBUG: 0,
|
||||
@@ -19,7 +19,7 @@ var COLORS = {
|
||||
|
||||
module.exports = function(_write, logLevel) {
|
||||
var logger = {};
|
||||
var lastChar = '\n';
|
||||
var lastChar = "\n";
|
||||
if (_.isString(logLevel)) logLevel = LEVELS[logLevel.toUpperCase()];
|
||||
|
||||
// Write a simple message
|
||||
@@ -47,14 +47,14 @@ module.exports = function(_write, logLevel) {
|
||||
var args = Array.prototype.slice.apply(arguments, [1]);
|
||||
var msg = logger.format.apply(logger, args);
|
||||
|
||||
if (lastChar == '\n') {
|
||||
if (lastChar == "\n") {
|
||||
msg = COLORS[levelKey](levelKey.toLowerCase()+":")+" "+msg;
|
||||
}
|
||||
|
||||
return logger.write(msg);
|
||||
};
|
||||
logger.logLn = function() {
|
||||
if (lastChar != '\n') logger.write("\n");
|
||||
if (lastChar != "\n") logger.write("\n");
|
||||
|
||||
var args = Array.prototype.slice.apply(arguments);
|
||||
args.push("\n");
|
||||
@@ -66,7 +66,7 @@ module.exports = function(_write, logLevel) {
|
||||
var args = Array.prototype.slice.apply(arguments, [1]);
|
||||
var msg = logger.format.apply(logger, args);
|
||||
if (arguments.length > 1) {
|
||||
logger.logLn(level, color.green('>> ') + msg.trim().replace(/\n/g, color.green('\n>> ')));
|
||||
logger.logLn(level, color.green(">> ") + msg.trim().replace(/\n/g, color.green("\n>> ")));
|
||||
} else {
|
||||
logger.log(level, color.green("OK"), "\n");
|
||||
}
|
||||
@@ -77,7 +77,7 @@ module.exports = function(_write, logLevel) {
|
||||
return logger.log(level, color.red("ERROR")+"\n");
|
||||
};
|
||||
|
||||
_.each(_.omit(LEVELS, 'DISABLED'), function(level, levelKey) {
|
||||
_.each(_.omit(LEVELS, "DISABLED"), function(level, levelKey) {
|
||||
levelKey = levelKey.toLowerCase();
|
||||
|
||||
logger[levelKey] = _.partial(logger.log, level);
|
||||
@@ -93,7 +93,7 @@ module.exports = function(_write, logLevel) {
|
||||
logger[levelKey].fail();
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return logger;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
var _ = require('lodash');
|
||||
var _ = require("lodash");
|
||||
|
||||
// Cleans up an article/chapter object
|
||||
// remove 'articles' attributes
|
||||
// remove "articles" attributes
|
||||
function clean(obj) {
|
||||
return obj && _.omit(obj, ['articles']);
|
||||
return obj && _.omit(obj, ["articles"]);
|
||||
}
|
||||
|
||||
function flattenChapters(chapters) {
|
||||
@@ -27,7 +27,7 @@ function navigation(summary, files) {
|
||||
files = _.isArray(files) ? files : (_.isString(files) ? [files] : null);
|
||||
|
||||
// List of all navNodes
|
||||
// Flatten chapters, then add in default README node if needed etc ...
|
||||
// Flatten chapters
|
||||
var navNodes = flattenChapters(summary.chapters);
|
||||
|
||||
// Mapping of prev/next for a give path
|
||||
@@ -39,8 +39,7 @@ function navigation(summary, files) {
|
||||
if(!current.exists) return null;
|
||||
|
||||
// Find prev
|
||||
prev = _.chain(navNodes)
|
||||
.slice(0, i)
|
||||
prev = _.chain(navNodes.slice(0, i))
|
||||
.reverse()
|
||||
.find(function(node) {
|
||||
return node.exists && !node.external;
|
||||
@@ -48,14 +47,12 @@ function navigation(summary, files) {
|
||||
.value();
|
||||
|
||||
// Find next
|
||||
next = _.chain(navNodes)
|
||||
.slice(i+1)
|
||||
next = _.chain(navNodes.slice(i+1))
|
||||
.find(function(node) {
|
||||
return node.exists && !node.external;
|
||||
})
|
||||
.value();
|
||||
|
||||
|
||||
return [current.path, {
|
||||
index: i,
|
||||
title: current.title,
|
||||
|
||||
+89
-78
@@ -5,13 +5,14 @@ var path = require('path');
|
||||
var cheerio = require('cheerio');
|
||||
var domSerializer = require('dom-serializer');
|
||||
var request = require('request');
|
||||
var crc = require("crc");
|
||||
var crc = require('crc');
|
||||
|
||||
var links = require('./links');
|
||||
var imgUtils = require('./images');
|
||||
var fs = require('./fs');
|
||||
var batch = require('./batch');
|
||||
var code = require('./code');
|
||||
|
||||
var parsableExtensions = require('gitbook-parsers').extensions;
|
||||
|
||||
// Render a cheerio dom as html
|
||||
var renderDom = function($, dom, options) {
|
||||
@@ -39,7 +40,8 @@ function replaceText($, el, search, replace, text_only ) {
|
||||
if ( node ) {
|
||||
|
||||
// Loop over all childNodes.
|
||||
do {
|
||||
while (node) {
|
||||
|
||||
// Only process text nodes.
|
||||
if ( node.nodeType === 3 ) {
|
||||
|
||||
@@ -67,17 +69,18 @@ function replaceText($, el, search, replace, text_only ) {
|
||||
}
|
||||
}
|
||||
|
||||
} while ( node = node.nextSibling );
|
||||
node = node.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
// Time to remove those elements!
|
||||
remove.length && $(remove).remove();
|
||||
if (remove.length) $(remove).remove();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function pregQuote( str ) {
|
||||
return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
|
||||
};
|
||||
return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, '\\$1');
|
||||
}
|
||||
|
||||
|
||||
// Adapt an html snippet to be relative to a base folder
|
||||
@@ -98,23 +101,24 @@ function normalizeHtml(src, options) {
|
||||
|
||||
// Find svg images to extract and process
|
||||
if (options.convertImages) {
|
||||
$("svg").each(function() {
|
||||
$('svg').each(function() {
|
||||
var content = renderDom($, $(this));
|
||||
var svgId = _.uniqueId("svg");
|
||||
var dest = svgId+".svg";
|
||||
var svgId = _.uniqueId('svg');
|
||||
var dest = svgId+'.svg';
|
||||
|
||||
// Generate filename
|
||||
dest = "/"+fs.getUniqueFilename(outputRoot, dest);
|
||||
dest = '/'+fs.getUniqueFilename(outputRoot, dest);
|
||||
|
||||
svgContent[dest] = '<?xml version="1.0" encoding="UTF-8"?>'+content;
|
||||
$(this).replaceWith($("<img>").attr("src", dest));
|
||||
$(this).replaceWith($('<img>').attr('src', dest));
|
||||
});
|
||||
}
|
||||
|
||||
// Find images to normalize
|
||||
$("img").each(function() {
|
||||
var origin = undefined;
|
||||
var src = $(this).attr("src");
|
||||
$('img').each(function() {
|
||||
var origin;
|
||||
var src = $(this).attr('src');
|
||||
|
||||
if (!src) return;
|
||||
var isExternal = links.isExternal(src);
|
||||
|
||||
@@ -128,13 +132,13 @@ function normalizeHtml(src, options) {
|
||||
// If image is external and ebook, then downlaod the images
|
||||
if (isExternal) {
|
||||
origin = src;
|
||||
src = "/"+crc.crc32(origin).toString(16)+path.extname(origin);
|
||||
src = '/'+crc.crc32(origin).toString(16)+path.extname(url.parse(origin).pathname);
|
||||
src = links.toAbsolute(src, options.base, options.output);
|
||||
isExternal = false;
|
||||
}
|
||||
|
||||
var ext = path.extname(src);
|
||||
var srcAbs = links.join("/", options.base, src);
|
||||
var srcAbs = links.join('/', options.base, src);
|
||||
|
||||
// Test image extension
|
||||
if (_.contains(imgUtils.INVALID, ext)) {
|
||||
@@ -143,29 +147,29 @@ function normalizeHtml(src, options) {
|
||||
src = imgConversionCache[outputRoot][srcAbs];
|
||||
} else {
|
||||
// Not converted yet
|
||||
var dest = "";
|
||||
var dest = '';
|
||||
|
||||
// Replace extension
|
||||
dest = links.join(path.dirname(srcAbs), path.basename(srcAbs, ext)+".png");
|
||||
dest = dest[0] == "/"? dest.slice(1) : dest;
|
||||
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
|
||||
dest = fs.getUniqueFilename(outputRoot, dest);
|
||||
|
||||
options.book.log.debug.ln("detect invalid image (will be converted to png):", srcAbs);
|
||||
options.book.log.debug.ln('detect invalid image (will be converted to png):', srcAbs);
|
||||
|
||||
// Add to cache
|
||||
imgConversionCache[outputRoot][srcAbs] = "/"+dest;
|
||||
imgConversionCache[outputRoot][srcAbs] = '/'+dest;
|
||||
|
||||
// Push to convert
|
||||
toConvert.push({
|
||||
origin: origin,
|
||||
content: svgContent[srcAbs],
|
||||
source: isExternal? srcAbs : path.join("./", srcAbs),
|
||||
dest: path.join("./", dest)
|
||||
source: isExternal? srcAbs : path.join('./', srcAbs),
|
||||
dest: path.join('./', dest)
|
||||
});
|
||||
|
||||
src = links.join("/", dest);
|
||||
src = links.join('/', dest);
|
||||
}
|
||||
|
||||
// Reset as relative to output
|
||||
@@ -176,17 +180,17 @@ function normalizeHtml(src, options) {
|
||||
// Need to downlaod image
|
||||
toConvert.push({
|
||||
origin: origin,
|
||||
source: path.join("./", srcAbs)
|
||||
source: path.join('./', srcAbs)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$(this).attr("src", src);
|
||||
$(this).attr('src', src);
|
||||
});
|
||||
|
||||
// Normalize links
|
||||
$("a").each(function() {
|
||||
var href = $(this).attr("href");
|
||||
$('a').each(function() {
|
||||
var href = $(this).attr('href');
|
||||
if (!href) return;
|
||||
|
||||
if (links.isAnchor(href)) {
|
||||
@@ -194,59 +198,75 @@ function normalizeHtml(src, options) {
|
||||
} else if (links.isRelative(href)) {
|
||||
var parts = url.parse(href);
|
||||
var absolutePath = links.join(options.base, parts.pathname);
|
||||
var anchor = parts.hash || "";
|
||||
var anchor = parts.hash || '';
|
||||
|
||||
|
||||
// If is in navigation relative: transform as content
|
||||
if (options.navigation[absolutePath]) {
|
||||
absolutePath = options.book.contentLink(absolutePath);
|
||||
}
|
||||
|
||||
// If md/adoc/rst files is not in summary
|
||||
// or for ebook, signal all files that are outside the summary
|
||||
else if (_.contains(parsableExtensions, path.extname(absolutePath)) ||
|
||||
_.contains(['epub', 'pdf', 'mobi'], options.book.options.generator)) {
|
||||
options.book.log.warn.ln('page', options.input, 'contains an hyperlink to resource outside spine \''+href+'\'');
|
||||
}
|
||||
|
||||
// Transform as absolute
|
||||
href = links.toAbsolute("/"+absolutePath, options.base, options.output)+anchor;
|
||||
href = links.toAbsolute('/'+absolutePath, options.base, options.output)+anchor;
|
||||
} else {
|
||||
// External links
|
||||
$(this).attr("target", "_blank");
|
||||
$(this).attr('target', '_blank');
|
||||
}
|
||||
|
||||
// Transform extension
|
||||
$(this).attr("href", href);
|
||||
$(this).attr('href', href);
|
||||
});
|
||||
|
||||
// Highlight code blocks
|
||||
$("code").each(function() {
|
||||
// Extract language
|
||||
$('code').each(function() {
|
||||
// Normalize language
|
||||
var lang = _.chain(
|
||||
($(this).attr("class") || "").split(" ")
|
||||
($(this).attr('class') || '').split(' ')
|
||||
)
|
||||
.map(function(cl) {
|
||||
if (cl.search("lang-") === 0) return cl.slice("lang-".length);
|
||||
// Markdown
|
||||
if (cl.search('lang-') === 0) return cl.slice('lang-'.length);
|
||||
|
||||
// Asciidoc
|
||||
if (cl.search('language-') === 0) return cl.slice('language-'.length);
|
||||
|
||||
return null;
|
||||
})
|
||||
.compact()
|
||||
.first()
|
||||
.value();
|
||||
|
||||
if (lang) {
|
||||
var html = code.highlight(
|
||||
lang,
|
||||
$(this).text()
|
||||
);
|
||||
var source = $(this).text();
|
||||
var blk = options.book.template.applyBlock('code', {
|
||||
body: source,
|
||||
kwargs: {
|
||||
language: lang
|
||||
}
|
||||
});
|
||||
|
||||
$(this).html(html);
|
||||
}
|
||||
if (blk.html === false) $(this).text(blk.body);
|
||||
else $(this).html(blk.body);
|
||||
});
|
||||
|
||||
// Replace glossary terms
|
||||
var glossary = _.sortBy(options.glossary, function(term) {
|
||||
return -term.name.length;
|
||||
});
|
||||
|
||||
_.each(glossary, function(term) {
|
||||
var r = new RegExp( "\\b(" + pregQuote(term.name.toLowerCase()) + ")\\b" , 'gi' );
|
||||
var r = new RegExp( '\\b(' + pregQuote(term.name.toLowerCase()) + ')\\b' , 'gi' );
|
||||
var includedInFiles = false;
|
||||
|
||||
$("*").each(function() {
|
||||
$('*').each(function() {
|
||||
// Ignore codeblocks
|
||||
if (_.contains(["code", "pre", "a"], this.name.toLowerCase())) return;
|
||||
if (_.contains(['code', 'pre', 'a'], this.name.toLowerCase())) return;
|
||||
|
||||
replaceText($, this, r, function(match) {
|
||||
// Add to files index in glossary
|
||||
@@ -255,7 +275,7 @@ function normalizeHtml(src, options) {
|
||||
term.files = term.files || [];
|
||||
term.files.push(options.navigation[options.input]);
|
||||
}
|
||||
return "<a href='"+links.toAbsolute("/GLOSSARY.html", options.base, options.output)+"#"+term.id+"' class='glossary-term' title='"+_.escape(term.description)+"'>"+match+"</a>";
|
||||
return '<a href=\''+links.toAbsolute('/GLOSSARY.html', options.base, options.output) + '#' + term.id+'\' class=\'glossary-term\' title=\''+_.escape(term.description)+'\'>'+match+'</a>';
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -264,14 +284,14 @@ function normalizeHtml(src, options) {
|
||||
html: renderDom($),
|
||||
images: toConvert
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Convert svg images to png
|
||||
function convertImages(images, options) {
|
||||
if (!options.convertImages) return Q();
|
||||
|
||||
var downloaded = [];
|
||||
options.book.log.debug.ln("convert ", images.length, "images to png");
|
||||
options.book.log.debug.ln('convert ', images.length, 'images to png');
|
||||
|
||||
return batch.execEach(images, {
|
||||
max: 100,
|
||||
@@ -283,9 +303,15 @@ function convertImages(images, options) {
|
||||
// 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, "...");
|
||||
options.book.log.debug('download image', image.origin, '...');
|
||||
downloaded.push(image.origin);
|
||||
return options.book.log.debug.promise(fs.writeStream(imgin, request(image.origin)));
|
||||
return options.book.log.debug.promise(fs.writeStream(imgin, request(image.origin)))
|
||||
.fail(function(err) {
|
||||
if (!_.isError(err)) err = new Error(err);
|
||||
|
||||
err.message = 'Fail downloading '+image.origin+': '+err.message;
|
||||
throw err;
|
||||
});
|
||||
})
|
||||
|
||||
// Write svg if content
|
||||
@@ -298,16 +324,15 @@ function convertImages(images, options) {
|
||||
.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, "...");
|
||||
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");
|
||||
options.book.log.debug.ok(images.length+' images converted with success');
|
||||
});
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Adapt page content to be relative to a base folder
|
||||
function normalizePage(sections, options) {
|
||||
@@ -319,16 +344,16 @@ function normalizePage(sections, options) {
|
||||
convertImages: false,
|
||||
|
||||
// Current file path
|
||||
input: ".",
|
||||
input: '.',
|
||||
|
||||
// Navigation to use to transform path
|
||||
navigation: {},
|
||||
|
||||
// Directory parent of the file currently in rendering process
|
||||
base: "./",
|
||||
base: './',
|
||||
|
||||
// Directory parent from the html output
|
||||
output: "./",
|
||||
output: './',
|
||||
|
||||
// Glossary terms
|
||||
glossary: []
|
||||
@@ -338,9 +363,9 @@ function normalizePage(sections, options) {
|
||||
var toConvert = [];
|
||||
|
||||
sections = _.map(sections, function(section) {
|
||||
if (section.type != "normal") return section;
|
||||
if (section.type != 'normal') return section;
|
||||
|
||||
var out = normalizeHtml(section.content, options);;
|
||||
var out = normalizeHtml(section.content, options);
|
||||
|
||||
toConvert = toConvert.concat(out.images);
|
||||
section.content = out.html;
|
||||
@@ -353,23 +378,9 @@ function normalizePage(sections, options) {
|
||||
return convertImages(toConvert, options);
|
||||
})
|
||||
.thenResolve(sections);
|
||||
};
|
||||
}
|
||||
|
||||
// Extract text from sections
|
||||
function extractText(sections) {
|
||||
return _.reduce(sections, function(prev, section) {
|
||||
if (section.type != "normal") return prev;
|
||||
|
||||
var $ = cheerio.load(section.content);
|
||||
$("*").each(function() {
|
||||
prev = prev+" "+$(this).text();
|
||||
});
|
||||
|
||||
return prev;
|
||||
}, "");
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
normalize: normalizePage,
|
||||
extractText: extractText
|
||||
normalize: normalizePage
|
||||
};
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
var _ = require("lodash");
|
||||
var path = require("path");
|
||||
|
||||
// Return true if file path is inside a folder
|
||||
function isInRoot(root, filename) {
|
||||
filename = path.normalize(filename);
|
||||
return (filename.substr(0, root.length) === root);
|
||||
}
|
||||
|
||||
// Resolve paths in a specific folder
|
||||
// Throw error if file is outside this folder
|
||||
function resolveInRoot(root) {
|
||||
var input, result, err;
|
||||
|
||||
input = _.chain(arguments)
|
||||
.toArray()
|
||||
.slice(1)
|
||||
.reduce(function(current, p) {
|
||||
// Handle path relative to book root ("/README.md")
|
||||
if (p[0] == "/" || p[0] == "\\") return p.slice(1);
|
||||
|
||||
return current? path.join(current, p) : path.normalize(p);
|
||||
}, "")
|
||||
.value();
|
||||
|
||||
result = path.resolve(root, input);
|
||||
|
||||
if (!isInRoot(root, result)) {
|
||||
err = new Error("EACCESS: \"" + result + "\" not in \"" + root + "\"");
|
||||
err.code = "EACCESS";
|
||||
throw err;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isInRoot: isInRoot,
|
||||
resolveInRoot: resolveInRoot
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
var _ = require("lodash");
|
||||
|
||||
// Returns from a navigation and a current file, a snapshot of current detailed state
|
||||
var calculProgress = function(navigation, current) {
|
||||
function calculProgress(navigation, current) {
|
||||
var n = _.size(navigation);
|
||||
var percent = 0, prevPercent = 0, currentChapter = null;
|
||||
var done = true;
|
||||
|
||||
+15
-17
@@ -1,11 +1,9 @@
|
||||
var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
|
||||
var events = require('events');
|
||||
var http = require('http');
|
||||
var send = require('send');
|
||||
var util = require('util');
|
||||
var url = require('url');
|
||||
var Q = require("q");
|
||||
var events = require("events");
|
||||
var http = require("http");
|
||||
var send = require("send");
|
||||
var util = require("util");
|
||||
var url = require("url");
|
||||
|
||||
var Server = function() {
|
||||
this.running = null;
|
||||
@@ -17,7 +15,7 @@ util.inherits(Server, events.EventEmitter);
|
||||
|
||||
// Return true if the server is running
|
||||
Server.prototype.isRunning = function() {
|
||||
return this.running != null;
|
||||
return !!this.running;
|
||||
};
|
||||
|
||||
// Stop the server
|
||||
@@ -57,25 +55,25 @@ Server.prototype.start = function(dir, port) {
|
||||
res.end(err.message);
|
||||
}
|
||||
|
||||
// Redirect to directory's index.html
|
||||
// Redirect to directory"s index.html
|
||||
function redirect() {
|
||||
res.statusCode = 301;
|
||||
res.setHeader('Location', req.url + '/');
|
||||
res.end('Redirecting to ' + req.url + '/');
|
||||
res.setHeader("Location", req.url + "/");
|
||||
res.end("Redirecting to " + req.url + "/");
|
||||
}
|
||||
|
||||
// Send file
|
||||
send(req, url.parse(req.url).pathname)
|
||||
.root(dir)
|
||||
.on('error', error)
|
||||
.on('directory', redirect)
|
||||
.on("error", error)
|
||||
.on("directory", redirect)
|
||||
.pipe(res);
|
||||
});
|
||||
|
||||
that.running.on('connection', function (socket) {
|
||||
that.running.on("connection", function (socket) {
|
||||
that.sockets.push(socket);
|
||||
socket.setTimeout(4000);
|
||||
socket.on('close', function () {
|
||||
socket.on("close", function () {
|
||||
that.sockets.splice(that.sockets.indexOf(socket), 1);
|
||||
});
|
||||
});
|
||||
@@ -91,6 +89,6 @@ Server.prototype.start = function(dir, port) {
|
||||
|
||||
return d.promise;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Server;
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
var _ = require("lodash");
|
||||
|
||||
function escapeShellArg(arg) {
|
||||
var ret = '';
|
||||
var ret = "";
|
||||
|
||||
ret = arg.replace(/"/g, '\\"');
|
||||
|
||||
@@ -11,7 +11,7 @@ function escapeShellArg(arg) {
|
||||
function optionsToShellArgs(options) {
|
||||
return _.chain(options)
|
||||
.map(function(value, key) {
|
||||
if (value == null || value === false) return null;
|
||||
if (value === null || value === undefined || value === false) return null;
|
||||
if (value === true) return key;
|
||||
return key+"="+escapeShellArg(value);
|
||||
})
|
||||
|
||||
+6
-5
@@ -1,9 +1,9 @@
|
||||
var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
var path = require('path');
|
||||
var chokidar = require('chokidar');
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var path = require("path");
|
||||
var chokidar = require("chokidar");
|
||||
|
||||
var parsers = require('gitbook-parsers')
|
||||
var parsers = require("gitbook-parsers");
|
||||
|
||||
function watch(dir) {
|
||||
var d = Q.defer();
|
||||
@@ -19,6 +19,7 @@ function watch(dir) {
|
||||
|
||||
var watcher = chokidar.watch(toWatch, {
|
||||
cwd: dir,
|
||||
ignored: "_book/**",
|
||||
ignoreInitial: true
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
var semver = require('semver');
|
||||
var pkg = require('../package.json');
|
||||
|
||||
var VERSION = pkg.version;
|
||||
var VERSION_STABLE = VERSION.replace(/\-(\S+)/g, '');
|
||||
|
||||
// Test if current current gitbook version satisfies a condition
|
||||
// We can't directly use samver.satisfies since it will break all plugins when gitbook version is a prerelease (beta, alpha)
|
||||
function satisfies(condition) {
|
||||
// Test with real version
|
||||
if (semver.satisfies(VERSION, condition)) return true;
|
||||
|
||||
// Test with future stable release
|
||||
return semver.satisfies(VERSION_STABLE, condition);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
satisfies: satisfies
|
||||
};
|
||||
+31
-17
@@ -1,27 +1,30 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "2.1.0",
|
||||
"version": "2.5.0-beta.6",
|
||||
"homepage": "https://www.gitbook.com",
|
||||
"description": "Library and cmd utility to generate GitBooks",
|
||||
"main": "lib/index.js",
|
||||
"dependencies": {
|
||||
"q": "1.0.1",
|
||||
"lunr": "0.5.7",
|
||||
"lodash": "3.5.0",
|
||||
"lodash": "3.10.1",
|
||||
"graceful-fs": "3.0.5",
|
||||
"resolve": "0.6.3",
|
||||
"fs-extra": "0.16.5",
|
||||
"fstream-ignore": "1.0.2",
|
||||
"gitbook-parsers": "0.7.6",
|
||||
"nunjucks": "mozilla/nunjucks#103513c294835bcbe64222de6db494e2555e294e",
|
||||
"gitbook-parsers": "0.8.5",
|
||||
"gitbook-plugin-highlight": "1.0.3",
|
||||
"gitbook-plugin-sharing": "1.0.1",
|
||||
"gitbook-plugin-search": "1.0.2",
|
||||
"gitbook-plugin-fontsettings": "1.0.2",
|
||||
"nunjucks": "2.1.0",
|
||||
"nunjucks-autoescape": "1.0.0",
|
||||
"nunjucks-filter": "1.0.0",
|
||||
"i18n": "0.5.0",
|
||||
"semver": "2.2.1",
|
||||
"semver": "5.0.1",
|
||||
"npmi": "0.1.1",
|
||||
"cheerio": "0.19.0",
|
||||
"gitbook-plugin-livereload": "0.0.1",
|
||||
"chokidar": "~1.0.1",
|
||||
"chokidar": "~1.0.5",
|
||||
"send": "0.2.0",
|
||||
"tiny-lr": "0.1.5",
|
||||
"tmp": "0.0.24",
|
||||
@@ -32,18 +35,29 @@
|
||||
"npm": "2.4.1",
|
||||
"dom-serializer": "0.1.0",
|
||||
"spawn-cmd": "0.0.2",
|
||||
"highlight.js": "8.4.0",
|
||||
"escape-string-regexp": "1.0.3"
|
||||
"escape-string-regexp": "1.0.3",
|
||||
"juice": "1.5.0",
|
||||
"jsonschema": "1.0.2",
|
||||
"json-schema-defaults": "0.1.1",
|
||||
"merge-defaults": "0.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "2.2.1",
|
||||
"should": "5.2.0",
|
||||
"grunt": "~0.4.2",
|
||||
"grunt-cli": "0.1.11",
|
||||
"grunt-contrib-copy": "0.5.0",
|
||||
"grunt-contrib-less": "~0.5.0",
|
||||
"grunt-contrib-requirejs": "0.4.1",
|
||||
"grunt-bower-install-simple": "0.9.2"
|
||||
"eslint": "1.5.0",
|
||||
"mocha": "2.3.2",
|
||||
"should": "7.1.0",
|
||||
"should-promised": "0.3.1",
|
||||
"gulp": "^3.8.11",
|
||||
"gulp-rename": "^1.2.2",
|
||||
"gulp-uglify": "1.1.0",
|
||||
"gulp-less": "3.0.2",
|
||||
"gulp-minify-css": "1.0.0",
|
||||
"gulp-util": "3.0.6",
|
||||
"browserify": "11.0.1",
|
||||
"merge-stream": "0.1.7",
|
||||
"vinyl-source-stream": "1.1.0",
|
||||
"jquery": "2.1.4",
|
||||
"mousetrap": "1.5.3",
|
||||
"font-awesome": "4.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node_modules/.bin/mocha --reporter spec --timeout 15000"
|
||||
|
||||
+22
-7
@@ -4,36 +4,51 @@ var path = require('path');
|
||||
var should = require('should');
|
||||
var cheerio = require('cheerio');
|
||||
|
||||
require('should-promised');
|
||||
|
||||
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.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" })));
|
||||
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 };
|
||||
this.params = { actual: 'HTML string', operator: 'valid html', message: description };
|
||||
var $ = cheerio.load(this.obj);
|
||||
|
||||
_.each(rules, function(validations, query) {
|
||||
validations = _.defaults(validations || {}, {
|
||||
// Select a specific element in the list of matched elements
|
||||
index: null,
|
||||
|
||||
// Check that there is the correct count of elements
|
||||
count: 1,
|
||||
|
||||
// Check attribute values
|
||||
attributes: {},
|
||||
|
||||
// Trim inner text
|
||||
trim: false,
|
||||
|
||||
// Check inner text
|
||||
text: undefined
|
||||
});
|
||||
|
||||
var $el = $(query);
|
||||
|
||||
// Select correct element
|
||||
if (_.isNumber(validations.index)) $el = $($el.get(validations.index));
|
||||
|
||||
// Test number of elements
|
||||
$el.length.should.be.equal(validations.count);
|
||||
|
||||
@@ -47,7 +62,7 @@ should.Assertion.add('html', function(rules, description) {
|
||||
// Test attributes
|
||||
_.each(validations.attributes, function(value, name) {
|
||||
var attr = $el.attr(name);
|
||||
should(attr).be.ok;
|
||||
should(attr).be.ok();
|
||||
attr.should.be.equal(value);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
# Readme
|
||||
|
||||
<p id="test-plugin-block-shortcuts-1">$$test_block1$$</p>
|
||||
<p id="test-plugin-block-shortcuts-2">{% include "./block.md" %}</p>
|
||||
|
||||
### Relative
|
||||
|
||||
<p id="t1">{% include "./hello.md" %}</p>
|
||||
<p id="t2">{% 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>
|
||||
<p id="t3">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md" %}</p>
|
||||
<p id="t4">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test2.md" %}</p>
|
||||
<p id="t5">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test3.md" %}</p>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
$$test_block2$$
|
||||
@@ -0,0 +1,16 @@
|
||||
# Readme
|
||||
|
||||
Block without language
|
||||
|
||||
```
|
||||
test 1
|
||||
```
|
||||
|
||||
Block with a language
|
||||
|
||||
```lang
|
||||
test 2
|
||||
```
|
||||
|
||||
Inline code: `test 3`
|
||||
Inline code with html: `<test>`
|
||||
@@ -0,0 +1 @@
|
||||
# Summary
|
||||
@@ -4,3 +4,6 @@
|
||||
* [Hello 2](hello2.md)
|
||||
* Hello 3
|
||||
* [Hello 4](hello3/hello4.md)
|
||||
* Hello 5
|
||||
* [Hello 6](hello3/hello5/hello6.md)
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
|
||||
var Plugin = require("../lib/plugin");
|
||||
var PLUGINS_ROOT = path.resolve(__dirname, "plugins");
|
||||
|
||||
describe("Code Highlighting", function () {
|
||||
var book, PAGE;
|
||||
|
||||
before(function() {
|
||||
return books.generate("highlight", "website", {
|
||||
prepare: function(_book) {
|
||||
book = _book;
|
||||
|
||||
var plugin = new Plugin(book, "replace_highlight");
|
||||
plugin.load("./replace_highlight", PLUGINS_ROOT);
|
||||
|
||||
book.plugins.load(plugin);
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
PAGE = fs.readFileSync(
|
||||
path.join(book.options.output, "index.html"),
|
||||
{ encoding: "utf-8" }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly replace highlighting", function() {
|
||||
PAGE.should.be.html({
|
||||
"code": {
|
||||
index: 0,
|
||||
text: "code_test 1\n_code"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly replace highlighting with language", function() {
|
||||
PAGE.should.be.html({
|
||||
"code": {
|
||||
index: 1,
|
||||
text: "lang_test 2\n_lang"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly replace highlighting for inline code", function() {
|
||||
PAGE.should.be.html({
|
||||
"code": {
|
||||
index: 2,
|
||||
text: "code_test 3_code"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly replace highlighting for inline code with html tags", function() {
|
||||
PAGE.should.be.html({
|
||||
"code": {
|
||||
index: 3,
|
||||
text: "code_<test>_code"
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+16
-11
@@ -1,32 +1,37 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
describe('Configuration', function () {
|
||||
it('should extract default title from README', function() {
|
||||
return books.parse("basic")
|
||||
return books.parse('basic')
|
||||
.then(function(book) {
|
||||
book.options.title.should.be.equal("Readme");
|
||||
book.options.title.should.be.equal('Readme');
|
||||
});
|
||||
});
|
||||
|
||||
it('should extract default description from README', function() {
|
||||
return books.parse("basic")
|
||||
return books.parse('basic')
|
||||
.then(function(book) {
|
||||
book.options.description.should.be.equal("Default description for the 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")
|
||||
return books.parse('config-json')
|
||||
.then(function(book) {
|
||||
book.options.title.should.be.equal("json-config");
|
||||
book.options.title.should.be.equal('json-config');
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly load from JavaScript (book.js)', function() {
|
||||
return books.parse("config-js")
|
||||
return books.parse('config-js')
|
||||
.then(function(book) {
|
||||
book.options.title.should.be.equal("js-config");
|
||||
book.options.title.should.be.equal('js-config');
|
||||
});
|
||||
});
|
||||
|
||||
it('should provide configuration on book.config.get', function() {
|
||||
return books.parse('basic')
|
||||
.then(function(book) {
|
||||
book.config.get('description').should.be.equal('Default description for the book.');
|
||||
book.getConfig('description').should.be.equal('Default description for the book.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+36
-11
@@ -5,37 +5,62 @@ describe('ConRefs', function () {
|
||||
var book, readme;
|
||||
|
||||
before(function() {
|
||||
return books.generate("conrefs", "website")
|
||||
return books.generate('conrefs', 'website')
|
||||
.then(function(_book) {
|
||||
book = _book;
|
||||
|
||||
readme = fs.readFileSync(
|
||||
path.join(book.options.output, "index.html"),
|
||||
{ encoding: "utf-8" }
|
||||
path.join(book.options.output, 'index.html'),
|
||||
{ encoding: 'utf-8' }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle local references', function() {
|
||||
readme.should.be.html({
|
||||
".page-inner p#t1": {
|
||||
'.page-inner p#t1': {
|
||||
count: 1,
|
||||
text: "Hello World",
|
||||
text: 'Hello World',
|
||||
trim: true
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle git references', function() {
|
||||
it('should handle local references with absolute paths', function() {
|
||||
readme.should.be.html({
|
||||
".page-inner p#t2": {
|
||||
'.page-inner p#t2': {
|
||||
count: 1,
|
||||
text: "Hello from git",
|
||||
text: 'Hello World',
|
||||
trim: true
|
||||
},
|
||||
".page-inner p#t3": {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly include file from git reference', function() {
|
||||
readme.should.be.html({
|
||||
'.page-inner p#t3': {
|
||||
count: 1,
|
||||
text: "First Hello. Hello from git",
|
||||
text: 'Hello from git',
|
||||
trim: true
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly handle deep include in git reference', function() {
|
||||
readme.should.be.html({
|
||||
'.page-inner p#t4': {
|
||||
count: 1,
|
||||
text: 'First Hello. Hello from git',
|
||||
trim: true
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly handle absolute include in git reference', function() {
|
||||
readme.should.be.html({
|
||||
'.page-inner p#t5': {
|
||||
count: 1,
|
||||
text: 'First Hello. Hello from git',
|
||||
trim: true
|
||||
}
|
||||
});
|
||||
|
||||
+31
-16
@@ -6,19 +6,23 @@ describe('eBook generator', function () {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
return books.generate("basic", "ebook")
|
||||
return books.generate('basic', 'ebook')
|
||||
.then(function(_book) {
|
||||
book = _book;
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly output a SUMMARY.html', function() {
|
||||
book.should.have.file("SUMMARY.html");
|
||||
book.should.have.file('SUMMARY.html');
|
||||
});
|
||||
|
||||
it('should correctly copy assets', function() {
|
||||
book.should.have.file("gitbook");
|
||||
book.should.have.file("gitbook/style.css");
|
||||
book.should.have.file('gitbook');
|
||||
book.should.have.file('gitbook/ebook.css');
|
||||
});
|
||||
|
||||
it('should not copy website assets', function() {
|
||||
book.should.not.have.file('gitbook/style.css');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,7 +30,7 @@ describe('eBook generator', function () {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
return books.generate("style-print", "ebook")
|
||||
return books.generate('style-print', 'ebook')
|
||||
.then(function(_book) {
|
||||
book = _book;
|
||||
});
|
||||
@@ -34,22 +38,33 @@ describe('eBook generator', function () {
|
||||
|
||||
it('should remove default print.css', function() {
|
||||
var PAGE = fs.readFileSync(
|
||||
path.join(book.options.output, "index.html"),
|
||||
{ encoding: "utf-8" }
|
||||
path.join(book.options.output, 'index.html'),
|
||||
{ encoding: 'utf-8' }
|
||||
);
|
||||
|
||||
// There are 2 styles (one from plugin-highlight and the new style)
|
||||
PAGE.should.be.html({
|
||||
"link": {
|
||||
count: 1,
|
||||
attributes: {
|
||||
href: "./styles/print.css"
|
||||
}
|
||||
'link': {
|
||||
count: 2
|
||||
}
|
||||
});
|
||||
|
||||
PAGE.should.be.html({
|
||||
'link[href=\'./styles/print.css\']': {
|
||||
count: 1
|
||||
}
|
||||
});
|
||||
|
||||
PAGE.should.be.html({
|
||||
'link[href="gitbook/plugins/gitbook-plugin-highlight/ebook.css"]': {
|
||||
count: 1
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly print.css', function() {
|
||||
book.should.have.file("styles");
|
||||
book.should.have.file("styles/print.css");
|
||||
it('should correctly copy print.css', function() {
|
||||
book.should.have.file('styles');
|
||||
book.should.have.file('styles/print.css');
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
describe('Formatting', function () {
|
||||
it('should provide formatting with book.formatString', function() {
|
||||
return books.parse('basic')
|
||||
.then(function(book) {
|
||||
return book.formatString('markdown', 'this is a **test**');
|
||||
})
|
||||
.then(function(content) {
|
||||
content.should.equal('<p>this is a <strong>test</strong></p>\n');
|
||||
});
|
||||
});
|
||||
});
|
||||
+6
-6
@@ -1,8 +1,8 @@
|
||||
var should = require('should');
|
||||
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() {
|
||||
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");
|
||||
|
||||
should.exist(parts);
|
||||
@@ -11,7 +11,7 @@ describe('GIT parser and getter', function () {
|
||||
parts.filepath.should.be.equal("test.md");
|
||||
});
|
||||
|
||||
it('should correctly parse an https url with a reference', function() {
|
||||
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");
|
||||
|
||||
should.exist(parts);
|
||||
@@ -20,7 +20,7 @@ describe('GIT parser and getter', function () {
|
||||
parts.filepath.should.be.equal("test.md");
|
||||
});
|
||||
|
||||
it('should correctly parse an ssh url', function() {
|
||||
it("should correctly parse an ssh url", function() {
|
||||
var parts = git.parseUrl("git+git@github.com:GitbookIO/gitbook.git/directory/README.md#e1594cde2c32e4ff48f6c4eff3d3d461743d74e1");
|
||||
|
||||
should.exist(parts);
|
||||
|
||||
+15
-15
@@ -1,8 +1,8 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
|
||||
describe('Glossary', function () {
|
||||
describe('Parsing', function() {
|
||||
describe("Glossary", function () {
|
||||
describe("Parsing", function() {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
@@ -12,13 +12,13 @@ describe('Glossary', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly list items', function() {
|
||||
it("should correctly list items", function() {
|
||||
book.should.have.property("glossary");
|
||||
book.glossary.should.have.lengthOf(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Generation', function() {
|
||||
describe("Generation", function() {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
@@ -28,11 +28,11 @@ describe('Glossary', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly generate a GLOSSARY.html', function() {
|
||||
it("should correctly generate a GLOSSARY.html", function() {
|
||||
book.should.have.file("GLOSSARY.html");
|
||||
});
|
||||
|
||||
describe('Page Integration', function() {
|
||||
describe("Page Integration", function() {
|
||||
var readme, page;
|
||||
|
||||
before(function() {
|
||||
@@ -46,9 +46,9 @@ describe('Glossary', function () {
|
||||
);
|
||||
});
|
||||
|
||||
it('should correctly replaced terms by links', function() {
|
||||
it("should correctly replaced terms by links", function() {
|
||||
readme.should.be.html({
|
||||
".page-inner a[href='GLOSSARY.html#test']": {
|
||||
".page-inner a[href=\"GLOSSARY.html#test\"]": {
|
||||
count: 1,
|
||||
text: "test",
|
||||
attributes: {
|
||||
@@ -58,15 +58,15 @@ describe('Glossary', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly replaced terms by links (relative)', function() {
|
||||
it("should correctly replaced terms by links (relative)", function() {
|
||||
page.should.be.html({
|
||||
".page-inner a[href='../GLOSSARY.html#test']": {
|
||||
".page-inner a[href=\"../GLOSSARY.html#test\"]": {
|
||||
count: 1
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should not replace terms in codeblocks', function() {
|
||||
it("should not replace terms in codeblocks", function() {
|
||||
readme.should.be.html({
|
||||
".page-inner code a": {
|
||||
count: 0
|
||||
@@ -74,9 +74,9 @@ describe('Glossary', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly select the longest term', function() {
|
||||
it("should correctly select the longest term", function() {
|
||||
readme.should.be.html({
|
||||
".page-inner a[href='GLOSSARY.html#test_long']": {
|
||||
".page-inner a[href=\"GLOSSARY.html#test_long\"]": {
|
||||
count: 1,
|
||||
text: "test long"
|
||||
}
|
||||
|
||||
+28
-18
@@ -1,15 +1,13 @@
|
||||
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 Book = require('../').Book;
|
||||
var LOG_LEVELS = require('../').LOG_LEVELS;
|
||||
|
||||
require("./assertions");
|
||||
require('./assertions');
|
||||
|
||||
|
||||
var BOOKS = {};
|
||||
@@ -17,30 +15,44 @@ var TMPDIR = os.tmpdir();
|
||||
|
||||
|
||||
// Generate and return a book
|
||||
function generateBook(bookId, test) {
|
||||
return parseBook(bookId, test)
|
||||
function generateBook(bookId, test, opts) {
|
||||
opts = _.defaults(opts || {}, {
|
||||
prepare: function() {}
|
||||
});
|
||||
|
||||
return parseBook(bookId, test, opts)
|
||||
.then(function(book) {
|
||||
return book.generate(test)
|
||||
|
||||
return Q(opts.prepare(book))
|
||||
.then(function() {
|
||||
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]);
|
||||
function parseBook(bookId, test, opts) {
|
||||
opts = _.defaults(opts || {}, {
|
||||
testId: ''
|
||||
});
|
||||
|
||||
BOOKS[bookId][test] = new Book(path.resolve(__dirname, "books", bookId), {
|
||||
test = test || 'website';
|
||||
var testId = [test, opts.testId].join('-');
|
||||
|
||||
BOOKS[bookId] = BOOKS[bookId] || {};
|
||||
if (BOOKS[bookId][testId]) return Q(BOOKS[bookId][testId]);
|
||||
|
||||
BOOKS[bookId][testId] = new Book(path.resolve(__dirname, 'books', bookId), {
|
||||
logLevel: LOG_LEVELS.DISABLED,
|
||||
config: {
|
||||
output: path.resolve(TMPDIR, bookId+"-"+test)
|
||||
output: path.resolve(TMPDIR, bookId+'-'+testId)
|
||||
}
|
||||
});
|
||||
|
||||
return BOOKS[bookId][test].parse()
|
||||
return BOOKS[bookId][testId].parse()
|
||||
.then(function() {
|
||||
return BOOKS[bookId][test];
|
||||
return BOOKS[bookId][testId];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -53,16 +65,14 @@ global.books = {
|
||||
// Cleanup all tests
|
||||
after(function() {
|
||||
return _.chain(BOOKS)
|
||||
.map(function(types, bookId) {
|
||||
.map(function(types) {
|
||||
return _.values(types);
|
||||
})
|
||||
.flatten()
|
||||
.reduce(function(prev, book) {
|
||||
return prev.then(function() {
|
||||
return fsUtil.remove(book.options.output);
|
||||
})
|
||||
});
|
||||
}, Q())
|
||||
.value();
|
||||
});
|
||||
|
||||
|
||||
|
||||
+14
-12
@@ -1,9 +1,9 @@
|
||||
var fs = require('fs');
|
||||
var _ = require('lodash');
|
||||
var path = require('path');
|
||||
var cheerio = require('cheerio');
|
||||
var fs = require("fs");
|
||||
var _ = require("lodash");
|
||||
var path = require("path");
|
||||
var cheerio = require("cheerio");
|
||||
|
||||
describe('Images', function () {
|
||||
describe("Images", function () {
|
||||
var book, readme, $, $img, srcs;
|
||||
|
||||
before(function() {
|
||||
@@ -17,38 +17,40 @@ describe('Images', function () {
|
||||
);
|
||||
$ = cheerio.load(readme);
|
||||
$img = $("img");
|
||||
srcs = $img.map(function() { return $(this).attr("src"); })
|
||||
srcs = $img.map(function() {
|
||||
return $(this).attr("src");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should detect all images', function() {
|
||||
it("should detect all images", function() {
|
||||
_.uniq(srcs).should.have.lengthOf(4);
|
||||
});
|
||||
|
||||
it('should keep image tags', function() {
|
||||
it("should keep image tags", function() {
|
||||
srcs.should.have.lengthOf(5);
|
||||
});
|
||||
|
||||
it('should not have .svg files', function() {
|
||||
it("should not have .svg files", function() {
|
||||
_.each(srcs, function(src) {
|
||||
path.extname(src).should.not.equal(".svg");
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly convert svg images to png', function() {
|
||||
it("should correctly convert svg images to png", function() {
|
||||
_.each(srcs, function(src) {
|
||||
book.should.have.file(src);
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle relative paths', function() {
|
||||
it("should handle relative paths", function() {
|
||||
var PAGE = fs.readFileSync(
|
||||
path.join(book.options.output, "folder/PAGE.html"),
|
||||
{ encoding: "utf-8" }
|
||||
);
|
||||
|
||||
PAGE.should.be.html({
|
||||
"img[src='../test.png']": {
|
||||
"img[src=\"../test.png\"]": {
|
||||
count: 1
|
||||
}
|
||||
});
|
||||
|
||||
+6
-5
@@ -9,15 +9,16 @@ describe('Init Books', function () {
|
||||
var initRoot;
|
||||
|
||||
before(function() {
|
||||
initRoot = path.resolve(__dirname, "books/init");
|
||||
initRoot = path.resolve(__dirname, 'books/init');
|
||||
return Book.init(initRoot, {
|
||||
logLevel: LOG_LEVELS.DISABLED,
|
||||
logLevel: LOG_LEVELS.DISABLED
|
||||
});
|
||||
});
|
||||
|
||||
it('should create all chapters', function() {
|
||||
should(fs.existsSync(path.resolve(initRoot, "hello.md"))).be.ok;
|
||||
should(fs.existsSync(path.resolve(initRoot, "hello2.md"))).be.ok;
|
||||
should(fs.existsSync(path.resolve(initRoot, "hello3/hello4.md"))).be.ok;
|
||||
should(fs.existsSync(path.resolve(initRoot, 'hello.md'))).be.ok();
|
||||
should(fs.existsSync(path.resolve(initRoot, 'hello2.md'))).be.ok();
|
||||
should(fs.existsSync(path.resolve(initRoot, 'hello3/hello4.md'))).be.ok();
|
||||
should(fs.existsSync(path.resolve(initRoot, 'hello3/hello5/hello6.md'))).be.ok();
|
||||
});
|
||||
});
|
||||
|
||||
+17
-17
@@ -1,8 +1,8 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
|
||||
describe('JSON generator', function () {
|
||||
describe('Basic Book', function() {
|
||||
describe("JSON generator", function () {
|
||||
describe("Basic Book", function() {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
@@ -12,15 +12,15 @@ describe('JSON generator', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly output a README.json', function() {
|
||||
it("should correctly output a README.json", function() {
|
||||
book.should.have.file("README.json");
|
||||
});
|
||||
|
||||
it('should output a valid json', function() {
|
||||
it("should output a valid json", function() {
|
||||
book.should.have.jsonfile("README.json");
|
||||
});
|
||||
|
||||
describe('Page Format', function() {
|
||||
describe("Page Format", function() {
|
||||
var page;
|
||||
|
||||
before(function() {
|
||||
@@ -32,25 +32,25 @@ describe('JSON generator', function () {
|
||||
);
|
||||
});
|
||||
|
||||
it('should contains valid section', function() {
|
||||
it("should contains valid section", function() {
|
||||
page.should.have.property("sections").with.lengthOf(1);
|
||||
page.sections[0].should.have.property("content").which.is.a.String;
|
||||
page.sections[0].should.have.property("type").which.is.a.String.which.equal("normal");
|
||||
page.sections[0].should.have.property("content").which.is.a.String();
|
||||
page.sections[0].should.have.property("type", "normal");
|
||||
});
|
||||
|
||||
it('should contains valid progress', function() {
|
||||
it("should contains valid progress", function() {
|
||||
page.should.have.property("progress");
|
||||
page.progress.should.have.property("chapters").with.lengthOf(1);
|
||||
page.progress.should.have.property("current");
|
||||
});
|
||||
|
||||
it('should contains no languages', function() {
|
||||
it("should contains no languages", function() {
|
||||
page.should.have.property("langs").with.lengthOf(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Multilingual Book', function() {
|
||||
describe("Multilingual Book", function() {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
@@ -60,19 +60,19 @@ describe('JSON generator', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly output READMEs', function() {
|
||||
it("should correctly output READMEs", function() {
|
||||
book.should.have.file("README.json");
|
||||
book.should.have.file("en/README.json");
|
||||
book.should.have.file("fr/README.json");
|
||||
});
|
||||
|
||||
it('should output valid json', function() {
|
||||
it("should output valid json", function() {
|
||||
book.should.have.jsonfile("README.json");
|
||||
book.should.have.jsonfile("en/README.json");
|
||||
book.should.have.jsonfile("fr/README.json");
|
||||
});
|
||||
|
||||
describe('Page Format', function() {
|
||||
describe("Page Format", function() {
|
||||
var page;
|
||||
|
||||
before(function() {
|
||||
@@ -84,7 +84,7 @@ describe('JSON generator', function () {
|
||||
);
|
||||
});
|
||||
|
||||
it('should contains no languages', function() {
|
||||
it("should contains no languages", function() {
|
||||
page.should.have.property("langs").with.lengthOf(2);
|
||||
});
|
||||
});
|
||||
|
||||
+5
-8
@@ -1,8 +1,5 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
describe('Languages', function () {
|
||||
describe('Parsing', function() {
|
||||
describe("Languages", function () {
|
||||
describe("Parsing", function() {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
@@ -12,7 +9,7 @@ describe('Languages', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly list languages', function() {
|
||||
it("should correctly list languages", function() {
|
||||
book.should.have.property("books");
|
||||
book.books.should.have.lengthOf(2);
|
||||
|
||||
@@ -21,7 +18,7 @@ describe('Languages', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Generation', function() {
|
||||
describe("Generation", function() {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
@@ -31,7 +28,7 @@ describe('Languages', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly create books', function() {
|
||||
it("should correctly create books", function() {
|
||||
book.should.have.file("index.html");
|
||||
book.should.have.file("en/index.html");
|
||||
book.should.have.file("fr/index.html");
|
||||
|
||||
+14
-16
@@ -1,13 +1,11 @@
|
||||
var should = require('should');
|
||||
var fs = require('fs');
|
||||
var _ = require('lodash');
|
||||
var path = require('path');
|
||||
var cheerio = require('cheerio');
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
var cheerio = require("cheerio");
|
||||
|
||||
var links = require("../lib/utils/links");
|
||||
|
||||
describe('Links', function () {
|
||||
it('should correctly test external links', function() {
|
||||
describe("Links", function () {
|
||||
it("should correctly test external links", function() {
|
||||
links.isExternal("http://google.fr").should.be.exactly(true);
|
||||
links.isExternal("https://google.fr").should.be.exactly(true);
|
||||
links.isExternal("test.md").should.be.exactly(false);
|
||||
@@ -15,32 +13,32 @@ describe('Links', function () {
|
||||
links.isExternal("/folder/test.md").should.be.exactly(false);
|
||||
});
|
||||
|
||||
it('should correctly detect anchor links', function() {
|
||||
it("should correctly detect anchor links", function() {
|
||||
links.isAnchor("#test").should.be.exactly(true);
|
||||
links.isAnchor(" #test").should.be.exactly(true);
|
||||
links.isAnchor("https://google.fr#test").should.be.exactly(false);
|
||||
links.isAnchor("test.md#test").should.be.exactly(false);
|
||||
});
|
||||
|
||||
describe('toAbsolute', function() {
|
||||
it('should correctly transform as absolute', function() {
|
||||
describe("toAbsolute", function() {
|
||||
it("should correctly transform as absolute", function() {
|
||||
links.toAbsolute("http://google.fr").should.be.equal("http://google.fr");
|
||||
links.toAbsolute("test.md", "./", "./").should.be.equal("test.md");
|
||||
links.toAbsolute("folder/test.md", "./", "./").should.be.equal("folder/test.md");
|
||||
});
|
||||
|
||||
it('should correctly handle windows path', function() {
|
||||
it("should correctly handle windows path", function() {
|
||||
links.toAbsolute("folder\\test.md", "./", "./").should.be.equal("folder/test.md");
|
||||
});
|
||||
|
||||
it('should correctly handle absolute path', function() {
|
||||
it("should correctly handle absolute path", function() {
|
||||
links.toAbsolute("/test.md", "./", "./").should.be.equal("test.md");
|
||||
links.toAbsolute("/test.md", "test", "test").should.be.equal("../test.md");
|
||||
links.toAbsolute("/sub/test.md", "test", "test").should.be.equal("../sub/test.md");
|
||||
});
|
||||
});
|
||||
|
||||
describe('page', function() {
|
||||
describe("page", function() {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
@@ -50,7 +48,7 @@ describe('Links', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly replace relative links', function() {
|
||||
it("should correctly replace relative links", function() {
|
||||
var readme = fs.readFileSync(
|
||||
path.join(book.options.output, "folder1/index.html"),
|
||||
{ encoding: "utf-8" }
|
||||
@@ -58,8 +56,8 @@ describe('Links', function () {
|
||||
var $ = cheerio.load(readme);
|
||||
var $a = $(".page-inner a");
|
||||
|
||||
$a.attr('href').should.be.exactly("../folder2/index.html");
|
||||
})
|
||||
$a.attr("href").should.be.exactly("../folder2/index.html");
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
var should = require("should");
|
||||
|
||||
describe("Navigation", function () {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
return books.parse("summary")
|
||||
.then(function(_book) {
|
||||
book = _book;
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly parse navigation as a map", function() {
|
||||
book.should.have.property("navigation");
|
||||
book.navigation.should.have.property("README.md");
|
||||
book.navigation.should.have.property("README.md");
|
||||
});
|
||||
|
||||
it("should correctly include filenames", function() {
|
||||
book.navigation.should.have.property("README.md");
|
||||
book.navigation.should.have.property("PAGE1.md");
|
||||
book.navigation.should.have.property("folder/PAGE2.md");
|
||||
book.navigation.should.not.have.property("NOTFOUND.md");
|
||||
});
|
||||
|
||||
it("should correctly detect next/prev for README", function() {
|
||||
var README = book.navigation["README.md"];
|
||||
|
||||
README.index.should.equal(0);
|
||||
README.should.have.property("next");
|
||||
should(README.prev).not.be.ok();
|
||||
|
||||
README.next.should.have.property("path");
|
||||
README.next.path.should.equal("PAGE1.md");
|
||||
});
|
||||
|
||||
it("should correctly detect next/prev a page", function() {
|
||||
var PAGE = book.navigation["PAGE1.md"];
|
||||
|
||||
PAGE.index.should.equal(1);
|
||||
PAGE.should.have.property("next");
|
||||
PAGE.should.have.property("prev");
|
||||
|
||||
PAGE.prev.should.have.property("path");
|
||||
PAGE.prev.path.should.equal("README.md");
|
||||
|
||||
PAGE.next.should.have.property("path");
|
||||
PAGE.next.path.should.equal("folder/PAGE2.md");
|
||||
});
|
||||
|
||||
it("should correctly detect next/prev for last page", function() {
|
||||
var PAGE = book.navigation["folder/PAGE2.md"];
|
||||
|
||||
PAGE.index.should.equal(2);
|
||||
PAGE.should.have.property("prev");
|
||||
should(PAGE.next).not.be.ok();
|
||||
|
||||
PAGE.prev.should.have.property("path");
|
||||
PAGE.prev.path.should.equal("PAGE1.md");
|
||||
});
|
||||
});
|
||||
+177
-37
@@ -1,26 +1,28 @@
|
||||
var _ = require('lodash');
|
||||
var fs = require('fs');
|
||||
var should = require('should');
|
||||
var path = require('path');
|
||||
|
||||
var Plugin = require('../lib/plugin');
|
||||
var parsers = require('gitbook-parsers');
|
||||
var PLUGINS_ROOT = path.resolve(__dirname, 'plugins');
|
||||
|
||||
describe('Plugins', function () {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
return books.parse("basic")
|
||||
.then(function(_book) {
|
||||
book = _book;
|
||||
});
|
||||
return books.parse('basic')
|
||||
.then(function(_book) {
|
||||
book = _book;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Invalid', function() {
|
||||
var plugin;
|
||||
|
||||
before(function() {
|
||||
plugin = new Plugin(book, "invalid");
|
||||
plugin.load("./invalid", PLUGINS_ROOT);
|
||||
plugin = new Plugin(book, 'invalid');
|
||||
plugin.load('./invalid', PLUGINS_ROOT);
|
||||
});
|
||||
|
||||
it('should be detected', function() {
|
||||
@@ -32,8 +34,8 @@ describe('Plugins', function () {
|
||||
var plugin;
|
||||
|
||||
before(function() {
|
||||
plugin = new Plugin(book, "empty");
|
||||
plugin.load("./empty", PLUGINS_ROOT);
|
||||
plugin = new Plugin(book, 'empty');
|
||||
plugin.load('./empty', PLUGINS_ROOT);
|
||||
});
|
||||
|
||||
it('should valid a plugin', function() {
|
||||
@@ -50,12 +52,43 @@ describe('Plugins', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Configuration', function() {
|
||||
var plugin;
|
||||
|
||||
before(function() {
|
||||
plugin = new Plugin(book, 'testconfig');
|
||||
plugin.load('./config', PLUGINS_ROOT);
|
||||
});
|
||||
|
||||
it('should throw error for invalid configuration', function() {
|
||||
return plugin.validateConfig({})
|
||||
.should.be.rejectedWith('Configuration Error: pluginsConfig.testconfig.testRequired is required');
|
||||
});
|
||||
|
||||
it('should throw error for invalid types', function() {
|
||||
return plugin.validateConfig({
|
||||
testRequired: 'hello'
|
||||
})
|
||||
.should.be.rejectedWith('Configuration Error: pluginsConfig.testconfig.testRequired is not of a type(s) number');
|
||||
});
|
||||
|
||||
it('should extend with default values', function() {
|
||||
return plugin.validateConfig({
|
||||
testRequired: 12
|
||||
})
|
||||
.should.be.fulfilledWith({
|
||||
hello: 'world',
|
||||
testRequired: 12
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Resources', function() {
|
||||
var plugin;
|
||||
|
||||
before(function() {
|
||||
plugin = new Plugin(book, "resources");
|
||||
plugin.load("./resources", PLUGINS_ROOT);
|
||||
plugin = new Plugin(book, 'resources');
|
||||
plugin.load('./resources', PLUGINS_ROOT);
|
||||
|
||||
return book.plugins.load(plugin);
|
||||
});
|
||||
@@ -66,29 +99,34 @@ describe('Plugins', function () {
|
||||
|
||||
describe('Website', function() {
|
||||
it('should return a valid list of resources', function() {
|
||||
return plugin.getResources("website")
|
||||
return plugin.getResources('website')
|
||||
.then(function(resources) {
|
||||
resources["js"].should.have.lengthOf(1);
|
||||
resources.js.should.have.lengthOf(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should extend books plugins', function() {
|
||||
var resources = book.plugins.resources("website");
|
||||
resources["js"].should.have.lengthOf(1);
|
||||
var resources = book.plugins.resources('website');
|
||||
resources.js.should.have.lengthOf(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('eBook', function() {
|
||||
it('should return a valid list of resources', function() {
|
||||
return plugin.getResources("ebook")
|
||||
return plugin.getResources('ebook')
|
||||
.then(function(resources) {
|
||||
resources["css"].should.have.lengthOf(1);
|
||||
resources.css.should.have.lengthOf(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should extend books plugins', function() {
|
||||
var resources = book.plugins.resources("ebook");
|
||||
resources["css"].should.have.lengthOf(1);
|
||||
var resources = book.plugins.resources('ebook');
|
||||
|
||||
// There is resources from highlight plugin and this plugin
|
||||
resources.css.should.have.lengthOf(2);
|
||||
should.exist(_.find(resources.css, {
|
||||
path: 'gitbook-plugin-resources/test'
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -97,8 +135,8 @@ describe('Plugins', function () {
|
||||
var plugin;
|
||||
|
||||
before(function() {
|
||||
plugin = new Plugin(book, "filters");
|
||||
plugin.load("./filters", PLUGINS_ROOT);
|
||||
plugin = new Plugin(book, 'filters');
|
||||
plugin.load('./filters', PLUGINS_ROOT);
|
||||
|
||||
return book.plugins.load(plugin);
|
||||
});
|
||||
@@ -111,21 +149,21 @@ describe('Plugins', function () {
|
||||
var filters = plugin.getFilters();
|
||||
|
||||
_.size(filters).should.equal(2);
|
||||
filters.should.have.property("hello");
|
||||
filters.should.have.property("helloCtx");
|
||||
filters.should.have.property('hello');
|
||||
filters.should.have.property('helloCtx');
|
||||
});
|
||||
|
||||
it('should correctly extend template filters', function() {
|
||||
return book.template.renderString('{{ "World"|hello }}')
|
||||
return book.template.renderString('{{ \'World\'|hello }}')
|
||||
.then(function(content) {
|
||||
content.should.equal("Hello World");
|
||||
content.should.equal('Hello World');
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly set book as context', function() {
|
||||
return book.template.renderString('{{ "root"|helloCtx }}')
|
||||
return book.template.renderString('{{ \'root\'|helloCtx }}')
|
||||
.then(function(content) {
|
||||
content.should.equal("root:"+book.root);
|
||||
content.should.equal('root:'+book.root);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -134,15 +172,15 @@ describe('Plugins', function () {
|
||||
var plugin;
|
||||
|
||||
before(function() {
|
||||
plugin = new Plugin(book, "blocks");
|
||||
plugin.load("./blocks", PLUGINS_ROOT);
|
||||
plugin = new Plugin(book, 'blocks');
|
||||
plugin.load('./blocks', PLUGINS_ROOT);
|
||||
|
||||
return book.plugins.load(plugin);
|
||||
});
|
||||
|
||||
var testTpl = function(str, args, options) {
|
||||
return book.template.renderString(str, args, options)
|
||||
.then(book.template.postProcess)
|
||||
.then(book.template.postProcess);
|
||||
};
|
||||
|
||||
it('should valid a plugin', function() {
|
||||
@@ -152,37 +190,139 @@ describe('Plugins', function () {
|
||||
it('should correctly extend template blocks', function() {
|
||||
return testTpl('{% test %}hello{% endtest %}')
|
||||
.then(function(content) {
|
||||
content.should.equal("testhellotest");
|
||||
content.should.equal('testhellotest');
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly accept shortcuts', function() {
|
||||
return testTpl('$$hello$$', {}, {
|
||||
type: "markdown"
|
||||
describe('Shortcuts', function() {
|
||||
it('should correctly accept shortcuts', function() {
|
||||
return testTpl('$$hello$$', {}, {
|
||||
type: 'markdown'
|
||||
})
|
||||
.then(function(content) {
|
||||
content.should.equal("testhellotest");
|
||||
content.should.equal('testhellotest');
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly apply shortcuts to included file', function() {
|
||||
return books.generate('conrefs', 'website', {
|
||||
testId: 'include-plugins',
|
||||
prepare: function(bookConref) {
|
||||
plugin = new Plugin(bookConref, 'blocks');
|
||||
plugin.load('./blocks', PLUGINS_ROOT);
|
||||
|
||||
return bookConref.plugins.load(plugin);
|
||||
}
|
||||
})
|
||||
.then(function(bookConref) {
|
||||
var readme = fs.readFileSync(
|
||||
path.join(bookConref.options.output, 'index.html'),
|
||||
{ encoding: 'utf-8' }
|
||||
);
|
||||
|
||||
readme.should.be.html({
|
||||
'.page-inner p#test-plugin-block-shortcuts-1': {
|
||||
count: 1,
|
||||
text: 'testtest_block1test',
|
||||
trim: true
|
||||
},
|
||||
'.page-inner p#test-plugin-block-shortcuts-2': {
|
||||
count: 1,
|
||||
text: 'testtest_block2test',
|
||||
trim: true
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should correctly extend template blocks with defined end', function() {
|
||||
return testTpl('{% test2 %}hello{% endtest2end %}')
|
||||
.then(function(content) {
|
||||
content.should.equal("test2hellotest2");
|
||||
content.should.equal('test2hellotest2');
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly extend template blocks with sub-blocks', function() {
|
||||
return testTpl('{% test3join separator=";" %}hello{% also %}world{% endtest3join %}')
|
||||
.then(function(content) {
|
||||
content.should.equal("hello;world");
|
||||
content.should.equal('hello;world');
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly extend template blocks with different sub-blocks', function() {
|
||||
return testTpl('{% test4join separator=";" %}hello{% also %}the{% finally %}world{% endtest4join %}')
|
||||
.then(function(content) {
|
||||
content.should.equal("hello;the;world");
|
||||
content.should.equal('hello;the;world');
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly extend template blocks with arguments (1)', function() {
|
||||
return testTpl('{% test5args "a" %}{% endtest5args %}')
|
||||
.then(function(content) {
|
||||
content.should.equal('test5atest5');
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly extend template blocks with arguments (2)', function() {
|
||||
return testTpl('{% test5args "a", "b" %}{% endtest5args %}')
|
||||
.then(function(content) {
|
||||
content.should.equal('test5a,btest5');
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly extend template blocks with arguments (3)', function() {
|
||||
return testTpl('{% test5args "a", "b", "c" %}{% endtest5args %}')
|
||||
.then(function(content) {
|
||||
content.should.equal('test5a,b,ctest5');
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly extend template blocks with args and kwargs', function() {
|
||||
return testTpl('{% test5kwargs "a", "b", "c", d="test", e="test2" %}{% endtest5kwargs %}')
|
||||
.then(function(content) {
|
||||
content.should.equal('test5a,b,c,d:test,e:test2,__keywords:truetest5');
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly extend template blocks with access to context', function() {
|
||||
return testTpl('{% set name = "john" %}{% test6context %}{% endtest6context %}', {})
|
||||
.then(function(content) {
|
||||
content.should.equal('test6johntest6');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Blocks without parsing', function() {
|
||||
var plugin;
|
||||
|
||||
before(function() {
|
||||
plugin = new Plugin(book, 'blocks');
|
||||
plugin.load('./blocks', PLUGINS_ROOT);
|
||||
|
||||
return book.plugins.load(plugin);
|
||||
});
|
||||
|
||||
var testTpl = function(markup, str, args, options) {
|
||||
var filetype = parsers.get(markup);
|
||||
|
||||
return book.template.renderString(str, args, options)
|
||||
.then(filetype.page).get('sections').get(0).get('content')
|
||||
.then(book.template.postProcess);
|
||||
};
|
||||
|
||||
it('should correctly process unparsable for markdown', function() {
|
||||
return testTpl('.md', '{% test %}**hello**{% endtest %}')
|
||||
.then(function(content) {
|
||||
content.should.equal('<p>test**hello**test</p>\n');
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly process unparsable for asciidoc', function() {
|
||||
return testTpl('.adoc', '{% test %}**hello**{% endtest %}')
|
||||
.then(function(content) {
|
||||
content.should.equal('<div class="paragraph">\n<p>test**hello**test</p>\n</div>');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,6 +36,26 @@ module.exports = {
|
||||
assert(blk.blocks[1].name, "finally");
|
||||
return [blk.body, blk.blocks[0].body, blk.blocks[1].body].join(blk.kwargs.separator);
|
||||
}
|
||||
}
|
||||
},
|
||||
"test5args": {
|
||||
process: function(blk) {
|
||||
return "test5"+blk.args.join(",")+"test5";
|
||||
}
|
||||
},
|
||||
"test5kwargs": {
|
||||
process: function(blk) {
|
||||
var s = blk.args.join(",");
|
||||
for (var key in blk.kwargs) {
|
||||
s = s + ","+key+":"+blk.kwargs[key];
|
||||
}
|
||||
|
||||
return "test5"+s+"test5";
|
||||
}
|
||||
},
|
||||
"test6context": {
|
||||
process: function() {
|
||||
return "test6"+(this.ctx.name)+"test6";
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = {};
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "gitbook-plugin-testconfig",
|
||||
"description": "Test plugin configuration",
|
||||
"main": "index.js",
|
||||
"version": "0.0.1",
|
||||
"engines": {
|
||||
"gitbook": "*"
|
||||
},
|
||||
"gitbook": {
|
||||
"properties": {
|
||||
"hello": {
|
||||
"type": "string",
|
||||
"default": "world"
|
||||
},
|
||||
"testRequired": {
|
||||
"type": "number",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
blocks: {
|
||||
"code": {
|
||||
process: function(blk) {
|
||||
var lang = blk.kwargs.language || "code";
|
||||
|
||||
return {
|
||||
body: lang+"_"+blk.body+"_"+lang,
|
||||
html: false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "gitbook-plugin-replace_highlight",
|
||||
"description": "Test replacing default code highlighter",
|
||||
"main": "index.js",
|
||||
"version": "0.0.1",
|
||||
"engines": {
|
||||
"gitbook": "*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
var path = require("path");
|
||||
|
||||
describe("Resolve Files", function () {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
return books.parse("basic")
|
||||
.then(function(_book) {
|
||||
book = _book;
|
||||
});
|
||||
});
|
||||
|
||||
describe("book.fileIsInBook", function() {
|
||||
it("should return true for correct paths", function() {
|
||||
book.fileIsInBook(path.join(book.root, "README.md")).should.equal(true);
|
||||
book.fileIsInBook(path.join(book.root, "styles/website.css")).should.equal(true);
|
||||
});
|
||||
|
||||
it("should return true for root folder", function() {
|
||||
book.fileIsInBook(path.join(book.root, "./")).should.equal(true);
|
||||
book.fileIsInBook(book.root).should.equal(true);
|
||||
});
|
||||
|
||||
it("should return false for files out of scope", function() {
|
||||
book.fileIsInBook(path.join(book.root, "../")).should.equal(false);
|
||||
book.fileIsInBook("README.md").should.equal(false);
|
||||
book.fileIsInBook(path.resolve(book.root, "../README.md")).should.equal(false);
|
||||
});
|
||||
|
||||
it("should correctly handle windows paths", function() {
|
||||
book.fileIsInBook(path.join(book.root, "\\styles\\website.css")).should.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("book.resolve", function() {
|
||||
it("should resolve a file to its absolute path", function() {
|
||||
book.resolve("README.md").should.equal(path.resolve(book.root, "README.md"));
|
||||
book.resolve("website/README.md").should.equal(path.resolve(book.root, "website/README.md"));
|
||||
});
|
||||
|
||||
it("should correctly handle windows paths", function() {
|
||||
book.resolve("styles\\website.css").should.equal(path.resolve(book.root, "styles\\website.css"));
|
||||
});
|
||||
|
||||
it("should correctly resolve all arguments", function() {
|
||||
book.resolve("test", "hello", "..", "README.md").should.equal(path.resolve(book.root, "test/README.md"));
|
||||
});
|
||||
|
||||
it("should correctly resolve to root folder", function() {
|
||||
book.resolve("test", "/README.md").should.equal(path.resolve(book.root, "README.md"));
|
||||
book.resolve("test", "\\README.md").should.equal(path.resolve(book.root, "README.md"));
|
||||
});
|
||||
|
||||
it("should throw an error for file out of book", function() {
|
||||
(function() {
|
||||
return book.resolve("../README.md");
|
||||
}).should.throw();
|
||||
});
|
||||
});
|
||||
});
|
||||
+12
-12
@@ -1,8 +1,8 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
|
||||
describe('Summary', function () {
|
||||
describe('Parsing', function() {
|
||||
describe("Summary", function () {
|
||||
describe("Parsing", function() {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
@@ -12,13 +12,13 @@ describe('Summary', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly list items', function() {
|
||||
it("should correctly list items", function() {
|
||||
book.should.have.property("summary");
|
||||
book.summary.should.have.property("chapters");
|
||||
book.summary.chapters.should.have.lengthOf(4);
|
||||
});
|
||||
|
||||
it('should correctly mark non-existant entries', function() {
|
||||
it("should correctly mark non-existant entries", function() {
|
||||
book.summary.chapters[0].exists.should.have.equal(true);
|
||||
book.summary.chapters[1].exists.should.have.equal(true);
|
||||
book.summary.chapters[2].exists.should.have.equal(true);
|
||||
@@ -26,7 +26,7 @@ describe('Summary', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Generation', function() {
|
||||
describe("Generation", function() {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
@@ -36,30 +36,30 @@ describe('Summary', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should create files according to summary', function() {
|
||||
it("should create files according to summary", function() {
|
||||
book.should.have.file("index.html");
|
||||
book.should.have.file("PAGE1.html");
|
||||
book.should.have.file("folder/PAGE2.html");
|
||||
});
|
||||
|
||||
it('should correctly output summary', function() {
|
||||
it("should correctly output summary", function() {
|
||||
var PAGE = fs.readFileSync(
|
||||
path.join(book.options.output, "index.html"),
|
||||
{ encoding: "utf-8" }
|
||||
);
|
||||
|
||||
PAGE.should.be.html({
|
||||
".book-summary .chapter[data-level='0'] a": {
|
||||
".book-summary .chapter[data-level=\"0\"] a": {
|
||||
attributes: {
|
||||
href: "./index.html"
|
||||
}
|
||||
},
|
||||
".book-summary .chapter[data-level='1'] a": {
|
||||
".book-summary .chapter[data-level=\"1\"] a": {
|
||||
attributes: {
|
||||
href: "./PAGE1.html"
|
||||
}
|
||||
},
|
||||
".book-summary .chapter[data-level='2'] a": {
|
||||
".book-summary .chapter[data-level=\"2\"] a": {
|
||||
attributes: {
|
||||
href: "./folder/PAGE2.html"
|
||||
}
|
||||
|
||||
+7
-7
@@ -1,6 +1,6 @@
|
||||
var pkg = require("../package.json");
|
||||
|
||||
describe('Templating', function () {
|
||||
describe("Templating", function () {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
@@ -12,19 +12,19 @@ describe('Templating', function () {
|
||||
|
||||
var testTpl = function(str, args, options) {
|
||||
return book.template.renderString(str, args, options)
|
||||
.then(book.template.postProcess)
|
||||
.then(book.template.postProcess);
|
||||
};
|
||||
|
||||
describe('Context', function() {
|
||||
it('should correctly have access to generator', function() {
|
||||
return testTpl('{{ gitbook.generator }}')
|
||||
describe("Context", function() {
|
||||
it("should correctly have access to generator", function() {
|
||||
return testTpl("{{ gitbook.generator }}")
|
||||
.then(function(content) {
|
||||
content.should.equal("website");
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly have access to gitbook version', function() {
|
||||
return testTpl('{{ gitbook.version }}')
|
||||
it("should correctly have access to gitbook version", function() {
|
||||
return testTpl("{{ gitbook.version }}")
|
||||
.then(function(content) {
|
||||
content.should.equal(pkg.version.toString());
|
||||
});
|
||||
|
||||
+9
-8
@@ -1,25 +1,26 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
describe('Website generator', function () {
|
||||
describe('Basic Book', function() {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
return books.generate("basic", "website")
|
||||
return books.generate('basic', 'website')
|
||||
.then(function(_book) {
|
||||
book = _book;
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly output an index.html', function() {
|
||||
book.should.have.file("index.html");
|
||||
book.should.have.file('index.html');
|
||||
});
|
||||
|
||||
it('should correctly copy assets', function() {
|
||||
book.should.have.file("gitbook");
|
||||
book.should.have.file("gitbook/app.js");
|
||||
book.should.have.file("gitbook/style.css");
|
||||
book.should.have.file('gitbook');
|
||||
book.should.have.file('gitbook/app.js');
|
||||
book.should.have.file('gitbook/style.css');
|
||||
});
|
||||
|
||||
it('should not copy ebook assets', function() {
|
||||
book.should.not.have.file('gitbook/ebook.css');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
.section.glossary>h1,.section.toc>h1{text-align:center}.page .section b,.page .section dl dt,.page .section strong,.page .section table th{font-weight:700}.link-inherit,.link-inherit:focus,.link-inherit:hover{color:inherit}.hidden{display:none}.section.toc>ol{margin:0;padding:0}.section.toc li{list-style:none}.section.toc li .inner{display:block;border-bottom:1px dotted #eee;margin-bottom:4px}.section.toc li .inner a,.section.toc li .inner span{padding-right:5px;border-bottom:1px solid #fff;margin-bottom:-1px}.section.toc li .inner .page{float:right;padding-left:5px}.section.toc li ol{margin:0;padding:0 0 0 2em}.dir-rtl .section.toc li .inner .page{float:left}.dir-rtl .section.toc li ol{margin:0;padding:0 2em 0 0}.section.glossary .glossary-entry{margin-bottom:40px}.section.glossary .glossary-entry h2 a,.section.glossary .glossary-entry h2 a:hover{color:inherit;text-decoration:none}.section.glossary .glossary-entry .glossary-index{list-style:none;margin:0;padding:0}.section.glossary .glossary-entry .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}body{font-family:sans-serif;color:#000;text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.page .section{display:block;word-wrap:break-word;overflow:hidden;color:#000;line-height:1.6}.page .section *{-moz-box-sizing:border-box;box-sizing:border-box}.page .section>:first-child{margin-top:0!important}.page .section>:last-child{margin-bottom:0!important}.page .section blockquote,.page .section code,.page .section figure,.page .section img,.page .section pre,.page .section table,.page .section tr{page-break-inside:avoid}.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section p{orphans:3;widows:3}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5{page-break-after:avoid}.page .section em{font-style:italic}.page .section blockquote,.page .section dl,.page .section ol,.page .section p,.page .section table,.page .section ul{margin-top:0;margin-bottom:.8em}.page .section a{color:#4183c4;text-decoration:none;background:0 0}.page .section a:active,.page .section a:focus,.page .section a:hover{outline:0;text-decoration:underline}.page .section img{border:0;max-width:100%}.page .section hr{height:4px;padding:0;margin:1.6em 0;overflow:hidden;background-color:#e7e7e7;border:none}.page .section hr:after,.page .section hr:before{display:table;content:" "}.page .section hr:after{clear:both}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section h6{margin-top:1.2em;margin-bottom:.8em;font-weight:700}.page .section h1{font-size:2em}.page .section h2{font-size:1.75em}.page .section h3{font-size:1.5em}.page .section h4{font-size:1.25em}.page .section h5,.page .section h6{font-size:1em}.page .section code,.page .section pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;direction:ltr;border:none}.page .section pre{overflow:auto;word-wrap:normal;margin:0 0 1.2em;padding:.85em 1em;background:#f7f7f7}.page .section pre>code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;font-size:.85em;white-space:pre;background:0 0}.page .section pre>code:after,.page .section pre>code:before{content:normal}.page .section code{padding:.2em;margin:0;font-size:.85em;background-color:#f7f7f7}.page .section code:after,.page .section code:before{letter-spacing:-.2em;content:"\00a0"}.page .section table{display:table;width:100%;border-collapse:collapse;border-spacing:0;overflow:auto}.page .section table td,.page .section table th{padding:6px 13px;border:1px solid #ddd}.page .section table tr{background-color:#fff;border-top:1px solid #ccc}.page .section table tr:nth-child(2n){background-color:#f8f8f8}.page .section ol,.page .section ul{padding:0 0 0 2em;margin-top:0;margin-bottom:0}.page .section ol ol,.page .section ol ul,.page .section ul ol,.page .section ul ul{margin-top:0;margin-bottom:0}.page .section ol ol{list-style-type:lower-roman}.page .section blockquote{margin:0;padding:0 15px;color:#858585;border-left:4px solid #e5e5e5}.page .section blockquote:first-child{margin-top:0}.page .section blockquote:last-child{margin-bottom:0}.page .section dl{padding:0}.page .section dl dt{padding:0;margin-top:.8em;font-style:italic}.page .section dl dd{padding:0 .8em;margin-bottom:.8em}.page .section dd{margin-left:0}.page .book-chapter{display:none}
|
||||
@@ -0,0 +1 @@
|
||||
.section.glossary>h1,.section.toc>h1{text-align:center}.page .section b,.page .section dl dt,.page .section strong,.page .section table th{font-weight:700}.link-inherit,.link-inherit:focus,.link-inherit:hover{color:inherit}.hidden{display:none}.section.toc>ol{margin:0;padding:0}.section.toc li{list-style:none}.section.toc li .inner{display:block;border-bottom:1px dotted #eee;margin-bottom:4px}.section.toc li .inner a,.section.toc li .inner span{padding-right:5px;border-bottom:1px solid #fff;margin-bottom:-1px}.section.toc li .inner .page{float:right;padding-left:5px}.section.toc li ol{margin:0;padding:0 0 0 2em}.dir-rtl .section.toc li .inner .page{float:left}.dir-rtl .section.toc li ol{margin:0;padding:0 2em 0 0}.section.glossary .glossary-entry{margin-bottom:40px}.section.glossary .glossary-entry h2 a,.section.glossary .glossary-entry h2 a:hover{color:inherit;text-decoration:none}.section.glossary .glossary-entry .glossary-index{list-style:none;margin:0;padding:0}.section.glossary .glossary-entry .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}body{font-family:serif;color:#000;text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.page .section{display:block;word-wrap:break-word;overflow:hidden;color:#000;line-height:1.6}.page .section *{-moz-box-sizing:border-box;box-sizing:border-box}.page .section>:first-child{margin-top:0!important}.page .section>:last-child{margin-bottom:0!important}.page .section blockquote,.page .section code,.page .section figure,.page .section img,.page .section pre,.page .section table,.page .section tr{page-break-inside:avoid}.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section p{orphans:3;widows:3}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5{page-break-after:avoid}.page .section em{font-style:italic}.page .section blockquote,.page .section dl,.page .section ol,.page .section p,.page .section table,.page .section ul{margin-top:0;margin-bottom:.8em}.page .section a{color:#4183c4;text-decoration:none;background:0 0}.page .section a:active,.page .section a:focus,.page .section a:hover{outline:0;text-decoration:underline}.page .section img{border:0;max-width:100%}.page .section hr{height:4px;padding:0;margin:1.6em 0;overflow:hidden;background-color:#e7e7e7;border:none}.page .section hr:after,.page .section hr:before{display:table;content:" "}.page .section hr:after{clear:both}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section h6{margin-top:1.2em;margin-bottom:.8em;font-weight:700}.page .section h1{font-size:2em}.page .section h2{font-size:1.75em}.page .section h3{font-size:1.5em}.page .section h4{font-size:1.25em}.page .section h5,.page .section h6{font-size:1em}.page .section code,.page .section pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;direction:ltr;border:none}.page .section pre{overflow:auto;word-wrap:normal;margin:0 0 1.2em;padding:.85em 1em;background:#f7f7f7}.page .section pre>code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;font-size:.85em;white-space:pre;background:0 0}.page .section pre>code:after,.page .section pre>code:before{content:normal}.page .section code{padding:.2em;margin:0;font-size:.85em;background-color:#f7f7f7}.page .section code:after,.page .section code:before{letter-spacing:-.2em;content:"\00a0"}.page .section table{display:table;width:100%;border-collapse:collapse;border-spacing:0;overflow:auto}.page .section table td,.page .section table th{padding:6px 13px;border:1px solid #ddd}.page .section table tr{background-color:#fff;border-top:1px solid #ccc}.page .section table tr:nth-child(2n){background-color:#f8f8f8}.page .section ol,.page .section ul{padding:0 0 0 2em;margin-top:0;margin-bottom:0}.page .section ol ol,.page .section ol ul,.page .section ul ol,.page .section ul ul{margin-top:0;margin-bottom:0}.page .section ol ol{list-style-type:lower-roman}.page .section blockquote{margin:0;padding:0 15px;color:#858585;border-left:4px solid #e5e5e5}.page .section blockquote:first-child{margin-top:0}.page .section blockquote:last-child{margin-bottom:0}.page .section dl{padding:0}.page .section dl dt{padding:0;margin-top:.8em;font-style:italic}.page .section dl dd{padding:0 .8em;margin-bottom:.8em}.page .section dd{margin-left:0}.page .book-chapter{display:none}
|
||||
@@ -0,0 +1 @@
|
||||
.section.glossary>h1,.section.toc>h1{text-align:center}.page .section b,.page .section dl dt,.page .section strong,.page .section table th{font-weight:700}.link-inherit,.link-inherit:focus,.link-inherit:hover{color:inherit}.hidden{display:none}.section.toc>ol{margin:0;padding:0}.section.toc li{list-style:none}.section.toc li .inner{display:block;border-bottom:1px dotted #eee;margin-bottom:4px}.section.toc li .inner a,.section.toc li .inner span{padding-right:5px;border-bottom:1px solid #fff;margin-bottom:-1px}.section.toc li .inner .page{float:right;padding-left:5px}.section.toc li ol{margin:0;padding:0 0 0 2em}.dir-rtl .section.toc li .inner .page{float:left}.dir-rtl .section.toc li ol{margin:0;padding:0 2em 0 0}.section.glossary .glossary-entry{margin-bottom:40px}.section.glossary .glossary-entry h2 a,.section.glossary .glossary-entry h2 a:hover{color:inherit;text-decoration:none}.section.glossary .glossary-entry .glossary-index{list-style:none;margin:0;padding:0}.section.glossary .glossary-entry .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}body{font-family:serif;color:#000;text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.page .section{display:block;word-wrap:break-word;overflow:hidden;color:#000;line-height:1.6}.page .section *{-moz-box-sizing:border-box;box-sizing:border-box}.page .section>:first-child{margin-top:0!important}.page .section>:last-child{margin-bottom:0!important}.page .section blockquote,.page .section code,.page .section figure,.page .section img,.page .section pre,.page .section table,.page .section tr{page-break-inside:avoid}.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section p{orphans:3;widows:3}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5{page-break-after:avoid}.page .section em{font-style:italic}.page .section blockquote,.page .section dl,.page .section ol,.page .section p,.page .section table,.page .section ul{margin-top:0;margin-bottom:.8em}.page .section a{color:#000;text-decoration:none;background:0 0}.page .section a:active,.page .section a:focus,.page .section a:hover{outline:0;text-decoration:underline}.page .section img{border:0;max-width:100%}.page .section hr{height:4px;padding:0;margin:1.6em 0;overflow:hidden;background-color:#e7e7e7;border:none}.page .section hr:after,.page .section hr:before{display:table;content:" "}.page .section hr:after{clear:both}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section h6{margin-top:1.2em;margin-bottom:.8em;font-weight:700}.page .section h1{font-size:2em}.page .section h2{font-size:1.75em}.page .section h3{font-size:1.5em}.page .section h4{font-size:1.25em}.page .section h5,.page .section h6{font-size:1em}.page .section code,.page .section pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;direction:ltr;border:none}.page .section pre{overflow:auto;word-wrap:normal;margin:0 0 1.2em;padding:.85em 1em;background:#f7f7f7}.page .section pre>code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;font-size:.85em;white-space:pre;background:0 0}.page .section pre>code:after,.page .section pre>code:before{content:normal}.page .section code{padding:.2em;margin:0;font-size:.85em;background-color:#f7f7f7}.page .section code:after,.page .section code:before{letter-spacing:-.2em;content:"\00a0"}.page .section table{display:table;width:100%;border-collapse:collapse;border-spacing:0;overflow:auto}.page .book-chapter,.page-toc li .inner span.page{display:none}.page .section table td,.page .section table th{padding:6px 13px;border:1px solid #ddd}.page .section table tr{background-color:#fff;border-top:1px solid #ccc}.page .section table tr:nth-child(2n){background-color:#f8f8f8}.page .section ol,.page .section ul{padding:0 0 0 2em;margin-top:0;margin-bottom:0}.page .section ol ol,.page .section ol ul,.page .section ul ol,.page .section ul ul{margin-top:0;margin-bottom:0}.page .section ol ol{list-style-type:lower-roman}.page .section blockquote{margin:0;padding:0 15px;color:#858585;border-left:4px solid #e5e5e5}.page .section blockquote:first-child{margin-top:0}.page .section blockquote:last-child{margin-bottom:0}.page .section dl{padding:0}.page .section dl dt{padding:0;margin-top:.8em;font-style:italic}.page .section dl dd{padding:0 .8em;margin-bottom:.8em}.page .section dd{margin-left:0}a{color:#000;text-decoration:underline!important}.page-toc li{list-style-type:lower-alpha}.page-toc li .inner{border:none}
|
||||
@@ -0,0 +1 @@
|
||||
.section.glossary>h1,.section.toc>h1{text-align:center}.page .section b,.page .section dl dt,.page .section strong,.page .section table th{font-weight:700}.link-inherit,.link-inherit:focus,.link-inherit:hover{color:inherit}.hidden{display:none}.section.toc>ol{margin:0;padding:0}.section.toc li{list-style:none}.section.toc li .inner{display:block;border-bottom:1px dotted #eee;margin-bottom:4px}.section.toc li .inner a,.section.toc li .inner span{padding-right:5px;border-bottom:1px solid #fff;margin-bottom:-1px}.section.toc li .inner .page{float:right;padding-left:5px}.section.toc li ol{margin:0;padding:0 0 0 2em}.dir-rtl .section.toc li .inner .page{float:left}.dir-rtl .section.toc li ol{margin:0;padding:0 2em 0 0}.section.glossary .glossary-entry{margin-bottom:40px}.section.glossary .glossary-entry h2 a,.section.glossary .glossary-entry h2 a:hover{color:inherit;text-decoration:none}.section.glossary .glossary-entry .glossary-index{list-style:none;margin:0;padding:0}.section.glossary .glossary-entry .glossary-index li{display:inline;margin:0 8px;white-space:nowrap}body{font-family:sans-serif;color:#000;text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.page .section{display:block;word-wrap:break-word;overflow:hidden;color:#000;line-height:1.6}.page .section *{-moz-box-sizing:border-box;box-sizing:border-box}.page .section>:first-child{margin-top:0!important}.page .section>:last-child{margin-bottom:0!important}.page .section blockquote,.page .section code,.page .section figure,.page .section img,.page .section pre,.page .section table,.page .section tr{page-break-inside:avoid}.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section p{orphans:3;widows:3}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5{page-break-after:avoid}.page .section em{font-style:italic}.page .section blockquote,.page .section dl,.page .section ol,.page .section p,.page .section table,.page .section ul{margin-top:0;margin-bottom:.8em}.page .section a{color:#4183c4;text-decoration:none;background:0 0}.page .section a:active,.page .section a:focus,.page .section a:hover{outline:0;text-decoration:underline}.page .section img{border:0;max-width:100%}.page .section hr{height:4px;padding:0;margin:1.6em 0;overflow:hidden;background-color:#e7e7e7;border:none}.page .section hr:after,.page .section hr:before{display:table;content:" "}.page .section hr:after{clear:both}.page .section h1,.page .section h2,.page .section h3,.page .section h4,.page .section h5,.page .section h6{margin-top:1.2em;margin-bottom:.8em;font-weight:700}.page .section h1{font-size:2em}.page .section h2{font-size:1.75em}.page .section h3{font-size:1.5em}.page .section h4{font-size:1.25em}.page .section h5,.page .section h6{font-size:1em}.page .section code,.page .section pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;direction:ltr;border:none}.page .section pre{overflow:auto;word-wrap:normal;margin:0 0 1.2em;padding:.85em 1em;background:#f7f7f7}.page .section pre>code{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;font-size:.85em;white-space:pre;background:0 0}.page .section pre>code:after,.page .section pre>code:before{content:normal}.page .section code{padding:.2em;margin:0;font-size:.85em;background-color:#f7f7f7}.page .section code:after,.page .section code:before{letter-spacing:-.2em;content:"\00a0"}.page .section table{display:table;width:100%;border-collapse:collapse;border-spacing:0;overflow:auto}.page .section table td,.page .section table th{padding:6px 13px;border:1px solid #ddd}.page .section table tr{background-color:#fff;border-top:1px solid #ccc}.page .section table tr:nth-child(2n){background-color:#f8f8f8}.page .section ol,.page .section ul{padding:0 0 0 2em;margin-top:0;margin-bottom:0}.page .section ol ol,.page .section ol ul,.page .section ul ol,.page .section ul ul{margin-top:0;margin-bottom:0}.page .section ol ol{list-style-type:lower-roman}.page .section blockquote{margin:0;padding:0 15px;color:#858585;border-left:4px solid #e5e5e5}.page .section blockquote:first-child{margin-top:0}.page .section blockquote:last-child{margin-bottom:0}.page .section dl{padding:0}.page .section dl dt{padding:0;margin-top:.8em;font-style:italic}.pdf-footer,.pdf-header{margin-top:20px;color:#aaa}.page .section dl dd{padding:0 .8em;margin-bottom:.8em}.page .section dd{margin-left:0}.page .book-chapter{display:none}.pdf-footer{padding-top:10px;border-top:1px solid #eee}.pdf-footer .footer-pages-count{float:right}.pdf-header{padding-bottom:10px;border-bottom:1px solid #eee}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Executable → Regular
|
Before Width: | Height: | Size: 248 KiB After Width: | Height: | Size: 248 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user