mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-28 21:18:57 +00:00
Compare commits
228 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d9e4c80a2 | |||
| a7f49015c1 | |||
| 68ed2631dc | |||
| 2814fea67e | |||
| 3388be9db6 | |||
| 9b89319820 | |||
| ad34089df5 | |||
| 7a4c6bc326 | |||
| 6ab386ae35 | |||
| 0abbfde912 | |||
| 0521defb63 | |||
| ea4d96610f | |||
| 4bc56564b4 | |||
| 013f7f09da | |||
| f082de1f92 | |||
| ad39dc8c0c | |||
| 481a76319f | |||
| 27a43d5aea | |||
| df1d5a0680 | |||
| 722109faf0 | |||
| d4dca7736a | |||
| 4e90becd52 | |||
| 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 | |||
| d895c63211 | |||
| 8c86da67ca | |||
| a9a230dfc3 | |||
| 6d53e52771 | |||
| 52e10b7cfe | |||
| addac85696 | |||
| 13fb949d68 | |||
| f1b593cfbd | |||
| f998e2dec9 | |||
| 2c0bd53a91 | |||
| 6dd7f6162f | |||
| 475b0bb2b3 | |||
| e6343935e3 | |||
| 946883fd4c | |||
| b1b7e3148b | |||
| 32203f6438 | |||
| f2d3c5d2ed | |||
| 7e2f0ed4bc | |||
| a877acf905 | |||
| 1a60778248 | |||
| cda4bb5bda | |||
| 7543781046 |
@@ -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
|
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
|
||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
theme/javascript/vendors
|
|
||||||
theme/vendors
|
|
||||||
|
|
||||||
# vim swapfile
|
# vim swapfile
|
||||||
*.swp
|
*.swp
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
sudo: false
|
sudo: false
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
|
- "4.1"
|
||||||
|
- "0.12"
|
||||||
- "0.11"
|
- "0.11"
|
||||||
- "0.10"
|
- "0.10"
|
||||||
before_install:
|
before_install:
|
||||||
|
|||||||
+21
-14
@@ -21,32 +21,39 @@ Translators
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
- German
|
- German
|
||||||
- Winnie (@winniehell)
|
- Winnie (@winniehell)
|
||||||
- Italian
|
- Italian
|
||||||
- Giulio Bonanome (@gbonanome)
|
- Giulio Bonanome (@gbonanome)
|
||||||
- Russian
|
- Russian
|
||||||
- Andrey Akinshin (@AndreyAkinshin)
|
- Andrey Akinshin (@AndreyAkinshin)
|
||||||
- Norwegian
|
- Norwegian
|
||||||
- Knut Melvær (@kmelve)
|
- Knut Melvær (@kmelve)
|
||||||
- Persian (Farsi)
|
- Persian (Farsi)
|
||||||
- Mohammad Hossein Mojtahedi (@mhm5000)
|
- Mohammad Hossein Mojtahedi (@mhm5000)
|
||||||
- Polish
|
- Polish
|
||||||
- Łukasz Szeremeta (@lszeremeta)
|
- Łukasz Szeremeta (@lszeremeta)
|
||||||
- Portuguese
|
- Portuguese
|
||||||
- Ryan O'Mullan (@RyanOM)
|
- Ryan O'Mullan (@RyanOM)
|
||||||
- Spanish
|
- Spanish
|
||||||
- Ryan O'Mullan (@RyanOM)
|
- Ryan O'Mullan (@RyanOM)
|
||||||
- Simplifiled Chinese
|
- Simplifiled Chinese
|
||||||
- Hu Hao (@howiehu)
|
- Hu Hao (@howiehu)
|
||||||
- Traditional Chinese
|
- Traditional Chinese
|
||||||
- Hu Hao (@howiehu)
|
- Hu Hao (@howiehu)
|
||||||
- French
|
- French
|
||||||
- Samy Pessé (@SamyPesse)
|
- Samy Pessé (@SamyPesse)
|
||||||
- Romanian
|
- Romanian
|
||||||
- Iancu Aurel (@awrelll)
|
- Iancu Aurel (@awrelll)
|
||||||
- Finnish
|
- Finnish
|
||||||
- Tommi Savikko (@savikko)
|
- Tommi Savikko (@savikko)
|
||||||
- Japanese
|
- Japanese
|
||||||
- Iancu Aurel (@awrelll)
|
- Iancu Aurel (@awrelll)
|
||||||
- Korean
|
- Korean
|
||||||
- Iancu Aurel (@awrelll)
|
- Iancu Aurel (@awrelll)
|
||||||
|
- SangYeob Yu (@deminoth)
|
||||||
|
- Vietnamese
|
||||||
|
- Hong Nguyen (@nghong)
|
||||||
|
- Hebrew
|
||||||
|
- @a-moses
|
||||||
|
- Ukrainian
|
||||||
|
- @Karnaukhov
|
||||||
|
|||||||
+90
@@ -2,6 +2,96 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## 2.5.2
|
||||||
|
- Fix custom stylesheets for ebook generation
|
||||||
|
- Trigger event `start` before `page.change`
|
||||||
|
|
||||||
|
## 2.5.1
|
||||||
|
- Fix calcul of `gitbook.state.root` when serving HTML index as `/`
|
||||||
|
|
||||||
|
## 2.5.0
|
||||||
|
- 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)
|
||||||
|
- Fix installation of plugins when using a pre-release
|
||||||
|
- Fix support for pre-releases
|
||||||
|
- Update asciidoc parser to remove git dependency
|
||||||
|
- Fix templating in imported content
|
||||||
|
- Fix querystring in image urls
|
||||||
|
- Normalize heading IDs like GitHub
|
||||||
|
- Use Arial as default font for PDFs
|
||||||
|
- Add `root`, `chapterTitle` and `filepath` to `gitbook.state` JS API
|
||||||
|
|
||||||
|
## 2.4.3
|
||||||
|
- Add ukrainian translation (`uk`)
|
||||||
|
- Add `book.json` configuration for maximum size of search index
|
||||||
|
- Improve reliability of summary parser
|
||||||
|
|
||||||
|
## 2.4.2
|
||||||
|
- Default plugins should not be installed by `gitbook install`
|
||||||
|
- Limit search index size to avoid crash during generation
|
||||||
|
- Fix code highlighting for html without language specified
|
||||||
|
- Fix warning message for gitbook version when building a multilingual book
|
||||||
|
|
||||||
|
## 2.4.1
|
||||||
|
- Fix disabling of default plugins, ex: `-highlight`
|
||||||
|
|
||||||
|
## 2.4.0
|
||||||
|
- Fix page being updated when user wants to open a link in a new tab
|
||||||
|
- Plugins can now replaced default code highlighter
|
||||||
|
- Add semantic information for screen readers (web version)
|
||||||
|
- Content references accept absolute paths, resolved to book folder
|
||||||
|
- Improve overall reliability
|
||||||
|
|
||||||
|
## 2.3.3
|
||||||
|
- Fix bug in SUMMARY parsing preventing multiple entries without filenames
|
||||||
|
|
||||||
|
## 2.3.2
|
||||||
|
- Fix blocks (like maths) in Asciidoc
|
||||||
|
- Fix error when checking gitbook version
|
||||||
|
|
||||||
|
## 2.3.1
|
||||||
|
- Fix black font color for ebooks (mobi, pdf and epub)
|
||||||
|
- Fix ISO code for korean language
|
||||||
|
- Fix korean translation
|
||||||
|
- Fix syntax highlighting for asciidoc
|
||||||
|
- Fix inline html escaping in markdown
|
||||||
|
- Add warning for file outside SUMMARY
|
||||||
|
- Force SUMMARY entries to be unique by filename
|
||||||
|
|
||||||
|
## 2.3.0
|
||||||
|
- Fix nunjucks issue with multiple `{% raw %}` blocks
|
||||||
|
- Fix crash when git conref failed
|
||||||
|
- Fix crash when failed to download remote image (better error message)
|
||||||
|
- Fix flicking effect when changing page (big UX improvement)
|
||||||
|
- Add Hebrew translation (`he`)
|
||||||
|
- Add utility method `book.config.get` for plugins
|
||||||
|
- Hooks `page:before` and `page` are no longer deprecated
|
||||||
|
- Remove webfonts to make website lighter
|
||||||
|
- Make glossary's order case insensitive
|
||||||
|
|
||||||
|
## 2.2.0
|
||||||
|
- Fix direction in code blocks (always LTR)
|
||||||
|
- Add options `chapterMark` and `pageBreaksBefore` for PDF
|
||||||
|
- Update code highlighting library
|
||||||
|
- `book.json` accessible as `config` in templating syntax
|
||||||
|
- Add Vietnamese translation (`vi`)
|
||||||
|
|
||||||
|
## 2.1.0
|
||||||
|
- Fix error in calcul of `levels` in table of contents, error introduced a few versions ago
|
||||||
|
- Add optional `styles/print.css` to replace `print.css` used in ebook
|
||||||
|
|
||||||
|
## 2.0.4
|
||||||
|
- Fix `{% raw %}`, got confused with "fake" variable declarations
|
||||||
|
- Fix title of language chooser
|
||||||
|
- Fix the X-UA-Compatible meta tag
|
||||||
|
- Move style sheets to the <head> section
|
||||||
|
|
||||||
## 2.0.3
|
## 2.0.3
|
||||||
- Fix `gitbook init` for SUMMARY with empty entries
|
- Fix `gitbook init` for SUMMARY with empty entries
|
||||||
- Fix escaping of code blocks in markdown
|
- Fix escaping of code blocks in markdown
|
||||||
|
|||||||
@@ -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)
|
[](http://badge.fury.io/js/gitbook)
|
||||||
[](https://travis-ci.org/GitbookIO/gitbook)
|
[](https://travis-ci.org/GitbookIO/gitbook)
|
||||||
[](https://ci.appveyor.com/project/SamyPesse/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).
|
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/).
|
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)
|
* [AsciiDoc Support](http://help.gitbook.com/format/asciidoc.html)
|
||||||
* [Variables and Templating](http://help.gitbook.com/format/templating.html)
|
* [Variables and Templating](http://help.gitbook.com/format/templating.html)
|
||||||
* [Content References](http://help.gitbook.com/format/conrefs.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:
|
* Interractive reader website:
|
||||||
* Search
|
* Search
|
||||||
* Font Settings (Serif, Sans Serif)
|
* 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
|
$ 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');
|
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('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('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('>> 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 Q = require('q');
|
||||||
var _ = require("lodash");
|
var _ = require('lodash');
|
||||||
var path = require("path");
|
var path = require('path');
|
||||||
var util = require("util");
|
var parsers = require('gitbook-parsers');
|
||||||
var lunr = require("lunr");
|
|
||||||
var parsers = require("gitbook-parsers");
|
|
||||||
var color = require("bash-color");
|
|
||||||
|
|
||||||
var fs = require("./utils/fs");
|
var fs = require('./utils/fs');
|
||||||
var parseNavigation = require("./utils/navigation");
|
var parseNavigation = require('./utils/navigation');
|
||||||
var parseProgress = require("./utils/progress");
|
var parseProgress = require('./utils/progress');
|
||||||
var pageUtil = require("./utils/page");
|
var pageUtil = require('./utils/page');
|
||||||
var batch = require("./utils/batch");
|
var pathUtil = require('./utils/path');
|
||||||
var links = require("./utils/links");
|
var links = require('./utils/links');
|
||||||
var i18n = require("./utils/i18n");
|
var i18n = require('./utils/i18n');
|
||||||
var logger = require("./utils/logger");
|
var logger = require('./utils/logger');
|
||||||
|
|
||||||
var Configuration = require("./configuration");
|
var Configuration = require('./configuration');
|
||||||
var TemplateEngine = require("./template");
|
var TemplateEngine = require('./template');
|
||||||
var Plugin = require("./plugin");
|
var PluginsList = require('./pluginslist');
|
||||||
var PluginsList = require("./pluginslist");
|
|
||||||
|
|
||||||
var generators = require("./generators");
|
var generators = require('./generators');
|
||||||
|
|
||||||
var Book = function(root, context, parent) {
|
var Book = function(root, context, parent) {
|
||||||
var that = this;
|
|
||||||
|
|
||||||
this.context = _.defaults(context || {}, {
|
this.context = _.defaults(context || {}, {
|
||||||
// Extend book configuration
|
// Extend book configuration
|
||||||
config: {},
|
config: {},
|
||||||
@@ -35,7 +29,7 @@ var Book = function(root, context, parent) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Log level
|
// Log level
|
||||||
logLevel: "info"
|
logLevel: 'info'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
@@ -49,7 +43,7 @@ var Book = function(root, context, parent) {
|
|||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
this.config = new Configuration(this, this.context.config);
|
this.config = new Configuration(this, this.context.config);
|
||||||
Object.defineProperty(this, "options", {
|
Object.defineProperty(this, 'options', {
|
||||||
get: function () {
|
get: function () {
|
||||||
return this.config.options;
|
return this.config.options;
|
||||||
}
|
}
|
||||||
@@ -83,21 +77,13 @@ var Book = function(root, context, parent) {
|
|||||||
this.readmeFile = null;
|
this.readmeFile = null;
|
||||||
this.langsFile = null;
|
this.langsFile = null;
|
||||||
|
|
||||||
// Search Index
|
|
||||||
this.searchIndex = lunr(function () {
|
|
||||||
this.ref('url');
|
|
||||||
|
|
||||||
this.field('title', { boost: 10 });
|
|
||||||
this.field('body');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Bind methods
|
// Bind methods
|
||||||
_.bindAll(this);
|
_.bindAll(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return string representation
|
// Return string representation
|
||||||
Book.prototype.toString = function() {
|
Book.prototype.toString = function() {
|
||||||
return "[Book "+this.root+"]";
|
return '[Book '+this.root+']';
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize and parse the book: config, summary, glossary
|
// Initialize and parse the book: config, summary, glossary
|
||||||
@@ -115,11 +101,11 @@ Book.prototype.parse = function() {
|
|||||||
return that.parseLangs()
|
return that.parseLangs()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
multilingual = that.langs.length > 0;
|
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
|
// Sub-books that inherit from the current book configuration
|
||||||
that.books = _.map(that.langs, function(lang) {
|
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(
|
return new Book(
|
||||||
path.join(that.root, lang.path),
|
path.join(that.root, lang.path),
|
||||||
_.merge({}, that.context, {
|
_.merge({}, that.context, {
|
||||||
@@ -129,7 +115,7 @@ Book.prototype.parse = function() {
|
|||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
that
|
that
|
||||||
)
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@@ -168,15 +154,15 @@ Book.prototype.generate = function(generator) {
|
|||||||
var that = this;
|
var that = this;
|
||||||
that.options.generator = generator || that.options.generator;
|
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()
|
return Q()
|
||||||
|
|
||||||
// Clean output folder
|
// Clean output folder
|
||||||
.then(function() {
|
.then(function() {
|
||||||
that.log.info("clean", that.options.generator, "generator");
|
that.log.info('clean', that.options.generator, 'generator');
|
||||||
return fs.clean(that.options.output)
|
return fs.clean(that.options.output)
|
||||||
.progress(function(p) {
|
.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() {
|
.then(function() {
|
||||||
that.log.info.ok();
|
that.log.info.ok();
|
||||||
@@ -186,12 +172,20 @@ Book.prototype.generate = function(generator) {
|
|||||||
// Create generator
|
// Create generator
|
||||||
.then(function() {
|
.then(function() {
|
||||||
var Generator = generators[generator];
|
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);
|
generator = new Generator(that);
|
||||||
|
|
||||||
return generator.prepare();
|
return generator.prepare();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Transform configuration
|
||||||
|
.then(function() {
|
||||||
|
return that.callHook('config', that.config.dump())
|
||||||
|
.then(function(newConfig) {
|
||||||
|
that.config.replace(newConfig);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
// Generate content
|
// Generate content
|
||||||
.then(function() {
|
.then(function() {
|
||||||
if (that.isMultilingual()) {
|
if (that.isMultilingual()) {
|
||||||
@@ -199,12 +193,12 @@ Book.prototype.generate = function(generator) {
|
|||||||
} else {
|
} else {
|
||||||
// Separate list of files into the different operations needed
|
// Separate list of files into the different operations needed
|
||||||
var ops = _.groupBy(that.files, function(file) {
|
var ops = _.groupBy(that.files, function(file) {
|
||||||
if (file[file.length -1] == "/") {
|
if (file[file.length -1] == '/') {
|
||||||
return "directories";
|
return 'directories';
|
||||||
} else if (_.contains(parsers.extensions, path.extname(file)) && that.navigation[file]) {
|
} else if (_.contains(parsers.extensions, path.extname(file)) && that.navigation[file]) {
|
||||||
return "content";
|
return 'content';
|
||||||
} else {
|
} else {
|
||||||
return "files";
|
return 'files';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -213,9 +207,9 @@ Book.prototype.generate = function(generator) {
|
|||||||
|
|
||||||
// First, let's create folder
|
// First, let's create folder
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return _.reduce(ops["directories"] || [], function(prev, folder) {
|
return _.reduce(ops.directories || [], function(prev, folder) {
|
||||||
return prev.then(function() {
|
return prev.then(function() {
|
||||||
that.log.debug.ln("transferring folder", folder);
|
that.log.debug.ln('transferring folder', folder);
|
||||||
return Q(generator.transferFolder(folder));
|
return Q(generator.transferFolder(folder));
|
||||||
});
|
});
|
||||||
}, Q());
|
}, Q());
|
||||||
@@ -223,19 +217,19 @@ Book.prototype.generate = function(generator) {
|
|||||||
|
|
||||||
// Then, let's copy other files
|
// Then, let's copy other files
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return Q.all(_.map(ops["files"] || [], function(file) {
|
return Q.all(_.map(ops.files || [], function(file) {
|
||||||
that.log.debug.ln("transferring file", file);
|
that.log.debug.ln('transferring file', file);
|
||||||
return Q(generator.transferFile(file));
|
return Q(generator.transferFile(file));
|
||||||
}));
|
}));
|
||||||
})
|
})
|
||||||
|
|
||||||
// Finally let's generate content
|
// Finally let's generate content
|
||||||
.then(function() {
|
.then(function() {
|
||||||
var nFiles = (ops["content"] || []).length;
|
var nFiles = (ops.content || []).length;
|
||||||
return _.reduce(ops["content"] || [], function(prev, file, i) {
|
return _.reduce(ops.content || [], function(prev, file, i) {
|
||||||
return prev.then(function() {
|
return prev.then(function() {
|
||||||
var p = ((i*100)/nFiles).toFixed(0)+"%";
|
var p = ((i*100)/nFiles).toFixed(0)+'%';
|
||||||
that.log.debug.ln("processing", file, p);
|
that.log.debug.ln('processing', file, p);
|
||||||
|
|
||||||
return Q(generator.convertFile(file))
|
return Q(generator.convertFile(file))
|
||||||
.fail(function(err) {
|
.fail(function(err) {
|
||||||
@@ -252,21 +246,21 @@ Book.prototype.generate = function(generator) {
|
|||||||
|
|
||||||
// Finish generation
|
// Finish generation
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return generator.callHook("finish:before");
|
return that.callHook('finish:before');
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return generator.finish();
|
return generator.finish();
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return generator.callHook("finish");
|
return that.callHook('finish');
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
that.log.info.ln("generation is finished");
|
that.log.info.ln('generation is finished');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate the output for a multilingual book
|
// Generate the output for a multilingual book
|
||||||
Book.prototype.generateMultiLingual = function(generator) {
|
Book.prototype.generateMultiLingual = function() {
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
return Q()
|
return Q()
|
||||||
@@ -287,26 +281,26 @@ Book.prototype.generateFile = function(output, options) {
|
|||||||
options = _.defaults(options || {}, {
|
options = _.defaults(options || {}, {
|
||||||
ebookFormat: path.extname(output).slice(1)
|
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()
|
return fs.tmp.dir()
|
||||||
.then(function(tmpDir) {
|
.then(function(tmpDir) {
|
||||||
book.setOutput(tmpDir);
|
book.setOutput(tmpDir);
|
||||||
|
|
||||||
return book.generate(options.ebookFormat)
|
return book.generate(options.ebookFormat)
|
||||||
.then(function(_options) {
|
.then(function() {
|
||||||
var copyFile = function(lang) {
|
var copyFile = function(lang) {
|
||||||
var _outputFile = output;
|
var _outputFile = output;
|
||||||
var _tmpDir = tmpDir;
|
var _tmpDir = tmpDir;
|
||||||
|
|
||||||
if (lang) {
|
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);
|
_tmpDir = path.join(_tmpDir, lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
book.log.debug.ln("copy ebook to", _outputFile);
|
book.log.debug.ln('copy ebook to', _outputFile);
|
||||||
return fs.copy(
|
return fs.copy(
|
||||||
path.join(_tmpDir, "index."+options.ebookFormat),
|
path.join(_tmpDir, 'index.'+options.ebookFormat),
|
||||||
_outputFile
|
_outputFile
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -326,7 +320,7 @@ Book.prototype.generateFile = function(output, options) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(function(n) {
|
.then(function(n) {
|
||||||
book.log.info.ok(n+" file(s) generated");
|
book.log.info.ok(n+' file(s) generated');
|
||||||
|
|
||||||
return fs.remove(tmpDir);
|
return fs.remove(tmpDir);
|
||||||
});
|
});
|
||||||
@@ -338,7 +332,7 @@ Book.prototype.generateFile = function(output, options) {
|
|||||||
Book.prototype.parseConfig = function() {
|
Book.prototype.parseConfig = function() {
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
that.log.info("loading book configuration....")
|
that.log.info('loading book configuration....');
|
||||||
return that.config.load()
|
return that.config.load()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
that.log.info.ok();
|
that.log.info.ok();
|
||||||
@@ -348,39 +342,38 @@ Book.prototype.parseConfig = function() {
|
|||||||
// Parse list of plugins
|
// Parse list of plugins
|
||||||
Book.prototype.parsePlugins = function() {
|
Book.prototype.parsePlugins = function() {
|
||||||
var that = this;
|
var that = this;
|
||||||
var failed = [];
|
|
||||||
|
|
||||||
// Load plugins
|
// Load plugins
|
||||||
return that.plugins.load(that.options.plugins)
|
return that.plugins.load(that.options.plugins)
|
||||||
.then(function() {
|
.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.info.ok(that.plugins.count()+' plugins loaded');
|
||||||
that.log.debug.ln("normalize plugins list");
|
that.log.debug.ln('normalize plugins list');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse readme to extract defaults title and description
|
// Parse readme to extract defaults title and description
|
||||||
Book.prototype.parseReadme = function() {
|
Book.prototype.parseReadme = function() {
|
||||||
var that = this;
|
var that = this;
|
||||||
var structure = that.config.getStructure("readme");
|
var structure = that.config.getStructure('readme');
|
||||||
that.log.debug.ln("start parsing readme:", structure);
|
that.log.debug.ln('start parsing readme:', structure);
|
||||||
|
|
||||||
return that.findFile(structure)
|
return that.findFile(structure)
|
||||||
.then(function(readme) {
|
.then(function(readme) {
|
||||||
if (!readme) throw "No README file";
|
if (!readme) throw 'No README file';
|
||||||
if (!_.contains(that.files, readme.path)) throw "README file is ignored";
|
if (!_.contains(that.files, readme.path)) throw 'README file is ignored';
|
||||||
|
|
||||||
that.readmeFile = readme.path;
|
that.readmeFile = readme.path;
|
||||||
that._defaultsStructure(that.readmeFile);
|
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)
|
return that.template.renderFile(that.readmeFile)
|
||||||
.then(function(content) {
|
.then(function(content) {
|
||||||
return readme.parser.readme(content)
|
return readme.parser.readme(content)
|
||||||
.fail(function(err) {
|
.fail(function(err) {
|
||||||
throw that.normError(err, {
|
throw that.normError(err, {
|
||||||
name: err.name || "Readme Parse Error",
|
name: err.name || 'Readme Parse Error',
|
||||||
fileName: that.readmeFile
|
fileName: that.readmeFile
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -397,8 +390,8 @@ Book.prototype.parseReadme = function() {
|
|||||||
Book.prototype.parseLangs = function() {
|
Book.prototype.parseLangs = function() {
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
var structure = that.config.getStructure("langs");
|
var structure = that.config.getStructure('langs');
|
||||||
that.log.debug.ln("start parsing languages index:", structure);
|
that.log.debug.ln('start parsing languages index:', structure);
|
||||||
|
|
||||||
return that.findFile(structure)
|
return that.findFile(structure)
|
||||||
.then(function(langs) {
|
.then(function(langs) {
|
||||||
@@ -407,13 +400,13 @@ Book.prototype.parseLangs = function() {
|
|||||||
that.langsFile = langs.path;
|
that.langsFile = langs.path;
|
||||||
that._defaultsStructure(that.langsFile);
|
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)
|
return that.template.renderFile(that.langsFile)
|
||||||
.then(function(content) {
|
.then(function(content) {
|
||||||
return langs.parser.langs(content)
|
return langs.parser.langs(content)
|
||||||
.fail(function(err) {
|
.fail(function(err) {
|
||||||
throw that.normError(err, {
|
throw that.normError(err, {
|
||||||
name: err.name || "Langs Parse Error",
|
name: err.name || 'Langs Parse Error',
|
||||||
fileName: that.langsFile
|
fileName: that.langsFile
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -428,19 +421,19 @@ Book.prototype.parseLangs = function() {
|
|||||||
Book.prototype.parseSummary = function() {
|
Book.prototype.parseSummary = function() {
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
var structure = that.config.getStructure("summary");
|
var structure = that.config.getStructure('summary');
|
||||||
that.log.debug.ln("start parsing summary:", structure);
|
that.log.debug.ln('start parsing summary:', structure);
|
||||||
|
|
||||||
return that.findFile(structure)
|
return that.findFile(structure)
|
||||||
.then(function(summary) {
|
.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
|
// Remove the summary from the list of files to parse
|
||||||
that.summaryFile = summary.path;
|
that.summaryFile = summary.path;
|
||||||
that._defaultsStructure(that.summaryFile);
|
that._defaultsStructure(that.summaryFile);
|
||||||
that.files = _.without(that.files, 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)
|
return that.template.renderFile(that.summaryFile)
|
||||||
.then(function(content) {
|
.then(function(content) {
|
||||||
return summary.parser.summary(content, {
|
return summary.parser.summary(content, {
|
||||||
@@ -450,7 +443,7 @@ Book.prototype.parseSummary = function() {
|
|||||||
})
|
})
|
||||||
.fail(function(err) {
|
.fail(function(err) {
|
||||||
throw that.normError(err, {
|
throw that.normError(err, {
|
||||||
name: err.name || "Summary Parse Error",
|
name: err.name || 'Summary Parse Error',
|
||||||
fileName: that.summaryFile
|
fileName: that.summaryFile
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -466,8 +459,8 @@ Book.prototype.parseSummary = function() {
|
|||||||
Book.prototype.parseGlossary = function() {
|
Book.prototype.parseGlossary = function() {
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
var structure = that.config.getStructure("glossary");
|
var structure = that.config.getStructure('glossary');
|
||||||
that.log.debug.ln("start parsing glossary: ", structure);
|
that.log.debug.ln('start parsing glossary: ', structure);
|
||||||
|
|
||||||
return that.findFile(structure)
|
return that.findFile(structure)
|
||||||
.then(function(glossary) {
|
.then(function(glossary) {
|
||||||
@@ -478,13 +471,13 @@ Book.prototype.parseGlossary = function() {
|
|||||||
that._defaultsStructure(that.glossaryFile);
|
that._defaultsStructure(that.glossaryFile);
|
||||||
that.files = _.without(that.files, 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)
|
return that.template.renderFile(that.glossaryFile)
|
||||||
.then(function(content) {
|
.then(function(content) {
|
||||||
return glossary.parser.glossary(content)
|
return glossary.parser.glossary(content)
|
||||||
.fail(function(err) {
|
.fail(function(err) {
|
||||||
throw that.normError(err, {
|
throw that.normError(err, {
|
||||||
name: err.name || "Glossary Parse Error",
|
name: err.name || 'Glossary Parse Error',
|
||||||
fileName: that.glossaryFile
|
fileName: that.glossaryFile
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -512,16 +505,16 @@ Book.prototype.parsePage = function(filename, options) {
|
|||||||
var interpolate = function(fn) {
|
var interpolate = function(fn) {
|
||||||
return Q(fn(page))
|
return Q(fn(page))
|
||||||
.then(function(_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 extension = path.extname(filename);
|
||||||
var filetype = parsers.get(extension);
|
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
|
// Type of parser used
|
||||||
page.type = filetype.name;
|
page.type = filetype.name;
|
||||||
@@ -535,7 +528,7 @@ Book.prototype.parsePage = function(filename, options) {
|
|||||||
// Progress in the book
|
// Progress in the book
|
||||||
page.progress = parseProgress(that.navigation, filename);
|
page.progress = parseProgress(that.navigation, filename);
|
||||||
|
|
||||||
that.log.debug.ln("render template", filename);
|
that.log.debug.ln('render template', filename);
|
||||||
|
|
||||||
// Read file content
|
// Read file content
|
||||||
return that.readFile(page.path)
|
return that.readFile(page.path)
|
||||||
@@ -562,7 +555,7 @@ Book.prototype.parsePage = function(filename, options) {
|
|||||||
.then(function(content) {
|
.then(function(content) {
|
||||||
page.content = 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);
|
return filetype.page(page.content);
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -570,11 +563,11 @@ Book.prototype.parsePage = function(filename, options) {
|
|||||||
.then(function(_page) {
|
.then(function(_page) {
|
||||||
return _.reduce(_page.sections, function(prev, section) {
|
return _.reduce(_page.sections, function(prev, section) {
|
||||||
return prev.then(function(_sections) {
|
return prev.then(function(_sections) {
|
||||||
return that.template.postProcess(section.content || "")
|
return that.template.postProcess(section.content || '')
|
||||||
.then(function(content) {
|
.then(function(content) {
|
||||||
section.content = content;
|
section.content = content;
|
||||||
return _sections.concat([section]);
|
return _sections.concat([section]);
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
}, Q([]));
|
}, Q([]));
|
||||||
})
|
})
|
||||||
@@ -599,7 +592,6 @@ Book.prototype.parsePage = function(filename, options) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
.then(function() {
|
.then(function() {
|
||||||
that.indexPage(page);
|
|
||||||
return page;
|
return page;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -622,29 +614,52 @@ Book.prototype.findFile = function(filename) {
|
|||||||
parser: parsers.get(ext),
|
parser: parsers.get(ext),
|
||||||
path: filepath
|
path: filepath
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
}, Q(null));
|
}, 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
|
// Check if a file exists in the book
|
||||||
Book.prototype.fileExists = function(filename) {
|
Book.prototype.fileExists = function(filename) {
|
||||||
return fs.exists(
|
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
|
// Read a file
|
||||||
Book.prototype.readFile = function(filename) {
|
Book.prototype.readFile = function(filename) {
|
||||||
return fs.readFile(
|
return fs.readFile(
|
||||||
path.join(this.root, filename),
|
this.resolve(filename),
|
||||||
{ encoding: "utf8" }
|
{ encoding: 'utf8' }
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return stat for a file
|
// Return stat for a file
|
||||||
Book.prototype.statFile = function(filename) {
|
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
|
// List all files in the book
|
||||||
@@ -682,7 +697,7 @@ Book.prototype.listAllFiles = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Return true if the book is a multilingual book
|
// Return true if the book is a multilingual book
|
||||||
Book.prototype.isMultilingual = function(filename) {
|
Book.prototype.isMultilingual = function() {
|
||||||
return this.books.length > 0;
|
return this.books.length > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -702,40 +717,38 @@ Book.prototype.isEntryPoint = function(fp) {
|
|||||||
return fp == this.readmeFile;
|
return fp == this.readmeFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Resolve a path in book
|
// Alias to book.config.get
|
||||||
Book.prototype.resolve = function(p) {
|
Book.prototype.getConfig = function(key, def) {
|
||||||
return path.resolve(this.root, p);
|
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
|
// Normalize a path to .html and convert README -> index
|
||||||
Book.prototype.contentPath = function(link) {
|
Book.prototype.contentPath = function(link) {
|
||||||
if (
|
if (
|
||||||
path.basename(link, path.extname(link)) == "README"
|
path.basename(link, path.extname(link)) == 'README' ||
|
||||||
|| link == this.readmeFile
|
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;
|
return link;
|
||||||
}
|
};
|
||||||
|
|
||||||
// Normalize a link to .html and convert README -> index
|
// Normalize a link to .html and convert README -> index
|
||||||
Book.prototype.contentLink = function(link) {
|
Book.prototype.contentLink = function(link) {
|
||||||
return links.normalize(this.contentPath(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
|
// Default structure paths to an extension
|
||||||
@@ -743,11 +756,11 @@ Book.prototype._defaultsStructure = function(filename) {
|
|||||||
var that = this;
|
var that = this;
|
||||||
var extension = path.extname(filename);
|
var extension = path.extname(filename);
|
||||||
|
|
||||||
that.readmeFile = that.readmeFile || that.config.getStructure("readme")+extension;
|
that.readmeFile = that.readmeFile || that.config.getStructure('readme')+extension;
|
||||||
that.summaryFile = that.summaryFile || that.config.getStructure("summary")+extension;
|
that.summaryFile = that.summaryFile || that.config.getStructure('summary')+extension;
|
||||||
that.glossaryFile = that.glossaryFile || that.config.getStructure("glossary")+extension;
|
that.glossaryFile = that.glossaryFile || that.config.getStructure('glossary')+extension;
|
||||||
that.langsFile = that.langsFile || that.config.getStructure("langs")+extension;
|
that.langsFile = that.langsFile || that.config.getStructure('langs')+extension;
|
||||||
}
|
};
|
||||||
|
|
||||||
// Change output path
|
// Change output path
|
||||||
Book.prototype.setOutput = function(p) {
|
Book.prototype.setOutput = function(p) {
|
||||||
@@ -760,7 +773,7 @@ Book.prototype.setOutput = function(p) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Translate a strign according to the book language
|
// Translate a strign according to the book language
|
||||||
Book.prototype.i18n = function(phrase) {
|
Book.prototype.i18n = function() {
|
||||||
var args = Array.prototype.slice.call(arguments);
|
var args = Array.prototype.slice.call(arguments);
|
||||||
return i18n.__.apply({}, [this.config.normalizeLanguage()].concat(args));
|
return i18n.__.apply({}, [this.config.normalizeLanguage()].concat(args));
|
||||||
};
|
};
|
||||||
@@ -779,87 +792,18 @@ Book.prototype.normError = function(err, opts, defs) {
|
|||||||
err.toString = function() {
|
err.toString = function() {
|
||||||
var attributes = [];
|
var attributes = [];
|
||||||
|
|
||||||
if (this.fileName) attributes.push("In file '"+this.fileName+"'");
|
if (this.fileName) attributes.push('In file \''+this.fileName+'\'');
|
||||||
if (this.lineNumber) attributes.push("Line "+this.lineNumber);
|
if (this.lineNumber) attributes.push('Line '+this.lineNumber);
|
||||||
if (this.columnNumber) attributes.push("Column "+this.columnNumber);
|
if (this.columnNumber) attributes.push('Column '+this.columnNumber);
|
||||||
return (this.name || "Error")+": "+this.message+((attributes.length > 0)? " ("+attributes.join(", ")+")" : "")
|
return (this.name || 'Error')+': '+this.message+((attributes.length > 0)? ' ('+attributes.join(', ')+')' : '');
|
||||||
};
|
};
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Init and return a book
|
// Call a hook in plugins
|
||||||
Book.init = function(root, opts) {
|
Book.prototype.callHook = function(name, data) {
|
||||||
var book = new Book(root, opts);
|
return this.plugins.hook(name, data);
|
||||||
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= Book;
|
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': 'Arial',
|
||||||
|
|
||||||
|
// 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
-168
@@ -1,38 +1,57 @@
|
|||||||
var _ = require("lodash");
|
var _ = require('lodash');
|
||||||
var Q = require("q");
|
var Q = require('q');
|
||||||
var path = require("path");
|
var path = require('path');
|
||||||
var semver = require('semver');
|
var semver = require('semver');
|
||||||
|
|
||||||
var pkg = require('../package.json');
|
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
|
// 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
|
// Normalize a list of plugins to use
|
||||||
function normalizePluginsList(plugins) {
|
function normalizePluginsList(plugins, addDefaults) {
|
||||||
// Normalize list to an array
|
// Normalize list to an array
|
||||||
plugins = _.isString(plugins) ? plugins.split(",") : (plugins || []);
|
plugins = _.isString(plugins) ? plugins.split(',') : (plugins || []);
|
||||||
|
|
||||||
// Remove empty parts
|
// Remove empty parts
|
||||||
plugins = _.compact(plugins);
|
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) {
|
plugins = _.map(plugins, function(plugin) {
|
||||||
if (plugin.name) return plugin;
|
if (plugin.name) return plugin;
|
||||||
|
|
||||||
var parts = plugin.split("@");
|
var parts = plugin.split('@');
|
||||||
|
var name = parts[0];
|
||||||
|
var version = parts[1];
|
||||||
return {
|
return {
|
||||||
'name': parts[0],
|
'name': name,
|
||||||
'version': parts[1] // optional
|
'version': version, // optional
|
||||||
}
|
'isDefault': isDefaultPlugin(name, version)
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// List plugins to remove
|
// List plugins to remove
|
||||||
var toremove = _.chain(plugins)
|
var toremove = _.chain(plugins)
|
||||||
.filter(function(plugin) {
|
.filter(function(plugin) {
|
||||||
return plugin.name.length > 0 && plugin.name[0] == "-";
|
return plugin.name.length > 0 && plugin.name[0] == '-';
|
||||||
})
|
})
|
||||||
.map(function(plugin) {
|
.map(function(plugin) {
|
||||||
return plugin.name.slice(1);
|
return plugin.name.slice(1);
|
||||||
@@ -40,54 +59,33 @@ function normalizePluginsList(plugins) {
|
|||||||
.value();
|
.value();
|
||||||
|
|
||||||
// Merge with defaults
|
// Merge with defaults
|
||||||
plugins = _.chain(plugins)
|
if (addDefaults !== false) {
|
||||||
.concat(_.map(defaultsPlugins, function(plugin) {
|
_.each(DEFAULT_PLUGINS, function(plugin) {
|
||||||
return { 'name': plugin }
|
if (_.find(plugins, { name: plugin })) {
|
||||||
}))
|
return;
|
||||||
.uniq()
|
}
|
||||||
.value();
|
|
||||||
|
|
||||||
// Build final list
|
plugins.push({
|
||||||
|
'name': plugin,
|
||||||
|
'isDefault': true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove plugin that start with '-'
|
||||||
plugins = _.filter(plugins, function(plugin) {
|
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;
|
return plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize a list of plugin name to use
|
|
||||||
function normalizePluginsNames(plugins) {
|
|
||||||
return _.pluck(normalizePluginsList(plugins), "name");
|
|
||||||
};
|
|
||||||
|
|
||||||
var Configuration = function(book, options) {
|
var Configuration = function(book, options) {
|
||||||
var that = this;
|
|
||||||
|
|
||||||
this.book = book;
|
this.book = book;
|
||||||
|
this.replace(options);
|
||||||
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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Read and parse the configuration
|
// Read and parse the configuration
|
||||||
@@ -96,11 +94,11 @@ Configuration.prototype.load = function() {
|
|||||||
|
|
||||||
return Q()
|
return Q()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
var configPath, config;
|
var configPath, _config;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
configPath = require.resolve(
|
configPath = require.resolve(
|
||||||
path.resolve(that.book.root, that.options.configFile)
|
that.book.resolve(that.options.configFile)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Invalidate node.js cache for livreloading
|
// Invalidate node.js cache for livreloading
|
||||||
@@ -118,18 +116,20 @@ Configuration.prototype.load = function() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
if (!semver.satisfies(pkg.version, that.options.gitbook)) {
|
if (!that.book.isSubBook()) {
|
||||||
throw "GitBook version doesn't satisfy version required by the book: "+that.options.gitbook;
|
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 == "*") {
|
}
|
||||||
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.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.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 = _.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)
|
// Default value for text direction (from language)
|
||||||
if (!that.options.direction) {
|
if (!that.options.direction) {
|
||||||
@@ -146,9 +146,43 @@ Configuration.prototype.extend = function(options) {
|
|||||||
_.extend(this.options, 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
|
// Get structure file
|
||||||
Configuration.prototype.getStructure = function(name) {
|
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
|
// Return normalized language
|
||||||
@@ -156,110 +190,17 @@ Configuration.prototype.normalizeLanguage = function() {
|
|||||||
return i18n.normalizeLanguage(this.options.language);
|
return i18n.normalizeLanguage(this.options.language);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Return a configuration
|
||||||
// Default configuration
|
Configuration.prototype.get = function(key, def) {
|
||||||
Configuration.DEFAULT = {
|
return _.get(this.options, key, def);
|
||||||
// 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",
|
|
||||||
"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": ""
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Update a configuration
|
||||||
|
Configuration.prototype.set = function(key, value) {
|
||||||
|
return _.set(this.options, key, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Default configuration
|
||||||
|
Configuration.DEFAULT = DEFAULT_CONFIG;
|
||||||
|
|
||||||
module.exports= Configuration;
|
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 _ = require('lodash');
|
||||||
var path = require("path");
|
var path = require('path');
|
||||||
var Q = require("q");
|
var Q = require('q');
|
||||||
var fs = require("./utils/fs");
|
var fs = require('./utils/fs');
|
||||||
|
|
||||||
var Plugin = require("./plugin");
|
|
||||||
|
|
||||||
var BaseGenerator = function(book) {
|
var BaseGenerator = function(book) {
|
||||||
this.book = book;
|
this.book = book;
|
||||||
|
|
||||||
Object.defineProperty(this, "options", {
|
Object.defineProperty(this, 'options', {
|
||||||
get: function () {
|
get: function () {
|
||||||
return this.book.options;
|
return this.book.options;
|
||||||
}
|
}
|
||||||
@@ -18,25 +16,25 @@ var BaseGenerator = function(book) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
BaseGenerator.prototype.callHook = function(name, data) {
|
BaseGenerator.prototype.callHook = function(name, data) {
|
||||||
return this.book.plugins.hook(name, data);
|
return this.book.callHook(name, data);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prepare the genertor
|
// Prepare the genertor
|
||||||
BaseGenerator.prototype.prepare = function() {
|
BaseGenerator.prototype.prepare = function() {
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
return that.callHook("init");
|
return that.callHook('init');
|
||||||
};
|
};
|
||||||
|
|
||||||
// Write a parsed file to the output
|
// Write a parsed file to the output
|
||||||
BaseGenerator.prototype.convertFile = function(input) {
|
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)
|
// Copy file to the output (non parsable)
|
||||||
BaseGenerator.prototype.transferFile = function(input) {
|
BaseGenerator.prototype.transferFile = function(input) {
|
||||||
return fs.copy(
|
return fs.copy(
|
||||||
path.join(this.book.root, input),
|
this.book.resolve(input),
|
||||||
path.join(this.options.output, input)
|
path.join(this.options.output, input)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -53,26 +51,26 @@ BaseGenerator.prototype.copyCover = function() {
|
|||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
return Q.all([
|
return Q.all([
|
||||||
fs.copy(path.join(that.book.root, "cover.jpg"), path.join(that.options.output, "cover.jpg")),
|
fs.copy(that.book.resolve('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_small.jpg'), path.join(that.options.output, 'cover_small.jpg'))
|
||||||
])
|
])
|
||||||
.fail(function() {
|
.fail(function() {
|
||||||
// If orignaly from multi-lang, try copy from parent
|
// If orignaly from multi-lang, try copy from parent
|
||||||
if (!that.book.isSubBook()) return;
|
if (!that.book.isSubBook()) return;
|
||||||
|
|
||||||
return Q.all([
|
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.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_small.jpg'), path.join(that.options.output, 'cover_small.jpg'))
|
||||||
]);
|
]);
|
||||||
})
|
})
|
||||||
.fail(function(err) {
|
.fail(function() {
|
||||||
return Q();
|
return Q();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// At teh end of the generation
|
// At teh end of the generation
|
||||||
BaseGenerator.prototype.finish = function() {
|
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;
|
module.exports = BaseGenerator;
|
||||||
|
|||||||
+89
-56
@@ -1,12 +1,13 @@
|
|||||||
var util = require("util");
|
var util = require('util');
|
||||||
var path = require("path");
|
var path = require('path');
|
||||||
var Q = require("q");
|
var Q = require('q');
|
||||||
var _ = require("lodash");
|
var _ = require('lodash');
|
||||||
|
var juice = require('juice');
|
||||||
var exec = require('child_process').exec;
|
var exec = require('child_process').exec;
|
||||||
|
|
||||||
var fs = require("../utils/fs");
|
var fs = require('../utils/fs');
|
||||||
var stringUtils = require("../utils/string");
|
var stringUtils = require('../utils/string');
|
||||||
var BaseGenerator = require("./website");
|
var BaseGenerator = require('./website');
|
||||||
|
|
||||||
var Generator = function(book, format) {
|
var Generator = function(book, format) {
|
||||||
BaseGenerator.apply(this, arguments);
|
BaseGenerator.apply(this, arguments);
|
||||||
@@ -15,10 +16,10 @@ var Generator = function(book, format) {
|
|||||||
this.ebookFormat = format;
|
this.ebookFormat = format;
|
||||||
|
|
||||||
// Resources namespace
|
// Resources namespace
|
||||||
this.namespace = "ebook";
|
this.namespace = 'ebook';
|
||||||
|
|
||||||
// Styles to use
|
// Styles to use
|
||||||
this.styles = _.compact(["ebook", this.ebookFormat]);
|
this.styles = _.compact(['print', 'ebook', this.ebookFormat]);
|
||||||
|
|
||||||
// Convert images (svg -> png)
|
// Convert images (svg -> png)
|
||||||
this.convertImages = true;
|
this.convertImages = true;
|
||||||
@@ -26,9 +27,9 @@ var Generator = function(book, format) {
|
|||||||
util.inherits(Generator, BaseGenerator);
|
util.inherits(Generator, BaseGenerator);
|
||||||
|
|
||||||
Generator.prototype.prepareTemplates = function() {
|
Generator.prototype.prepareTemplates = function() {
|
||||||
this.templates["page"] = this.book.plugins.template("ebook:page") || path.resolve(this.options.theme, 'templates/ebook/page.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.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.glossary = this.book.plugins.template('ebook:glossary') || path.resolve(this.options.theme, 'templates/ebook/glossary.html');
|
||||||
|
|
||||||
return Q();
|
return Q();
|
||||||
};
|
};
|
||||||
@@ -37,8 +38,39 @@ Generator.prototype.prepareTemplates = function() {
|
|||||||
Generator.prototype.writeSummary = function() {
|
Generator.prototype.writeSummary = function() {
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
that.book.log.info.ln("write SUMMARY.html");
|
that.book.log.info.ln('write SUMMARY.html');
|
||||||
return this._writeTemplate(this.templates["summary"], {}, path.join(this.options.output, "SUMMARY.html"));
|
return this._writeTemplate(this.templates.summary, {}, path.join(this.options.output, 'SUMMARY.html'));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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.join('\n\n')
|
||||||
|
});
|
||||||
|
|
||||||
|
return tpl;
|
||||||
};
|
};
|
||||||
|
|
||||||
Generator.prototype.finish = function() {
|
Generator.prototype.finish = function() {
|
||||||
@@ -52,70 +84,71 @@ Generator.prototype.finish = function() {
|
|||||||
.then(function() {
|
.then(function() {
|
||||||
if (!that.ebookFormat) return Q();
|
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 = {
|
var _options = {
|
||||||
"--cover": that.options.cover,
|
'--cover': that.options.cover,
|
||||||
"--title": that.options.title,
|
'--title': that.options.title,
|
||||||
"--comments": that.options.description,
|
'--comments': that.options.description,
|
||||||
"--isbn": that.options.isbn,
|
'--isbn': that.options.isbn,
|
||||||
"--authors": that.options.author,
|
'--authors': that.options.author,
|
||||||
"--language": that.options.language,
|
'--language': that.options.language,
|
||||||
"--book-producer": "GitBook",
|
'--book-producer': 'GitBook',
|
||||||
"--publisher": "GitBook",
|
'--publisher': 'GitBook',
|
||||||
"--chapter": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter ')]",
|
'--chapter': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter \')]',
|
||||||
"--chapter-mark": "pagebreak",
|
'--level1-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-1 \')]',
|
||||||
"--page-breaks-before": "/",
|
'--level2-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-2 \')]',
|
||||||
"--level1-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-1 ')]",
|
'--level3-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-3 \')]',
|
||||||
"--level2-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-2 ')]",
|
'--no-chapters-in-toc': true,
|
||||||
"--level3-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-3 ')]",
|
'--max-levels': '1',
|
||||||
"--no-chapters-in-toc": true,
|
'--breadth-first': true
|
||||||
"--max-levels": "1",
|
|
||||||
"--breadth-first": true
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (that.ebookFormat == "pdf") {
|
if (that.ebookFormat == 'pdf') {
|
||||||
var pdfOptions = that.options.pdf;
|
var pdfOptions = that.options.pdf;
|
||||||
|
|
||||||
_.extend(_options, {
|
_.extend(_options, {
|
||||||
"--margin-left": String(pdfOptions.margin.left),
|
'--chapter-mark': String(pdfOptions.chapterMark),
|
||||||
"--margin-right": String(pdfOptions.margin.right),
|
'--page-breaks-before': String(pdfOptions.pageBreaksBefore),
|
||||||
"--margin-top": String(pdfOptions.margin.top),
|
'--margin-left': String(pdfOptions.margin.left),
|
||||||
"--margin-bottom": String(pdfOptions.margin.bottom),
|
'--margin-right': String(pdfOptions.margin.right),
|
||||||
"--pdf-default-font-size": String(pdfOptions.fontSize),
|
'--margin-top': String(pdfOptions.margin.top),
|
||||||
"--pdf-mono-font-size": String(pdfOptions.fontSize),
|
'--margin-bottom': String(pdfOptions.margin.bottom),
|
||||||
"--paper-size": String(pdfOptions.paperSize),
|
'--pdf-default-font-size': String(pdfOptions.fontSize),
|
||||||
"--pdf-page-numbers": Boolean(pdfOptions.pageNumbers),
|
'--pdf-mono-font-size': String(pdfOptions.fontSize),
|
||||||
"--pdf-header-template": String(pdfOptions.headerTemplate) || "<p class='header'><span>"+that.options.title+"</span></p>",
|
'--paper-size': String(pdfOptions.paperSize),
|
||||||
"--pdf-footer-template": String(pdfOptions.footerTemplate) || "<p class='footer'><span>_SECTION_</span> <span style='float:right;'>_PAGENUM_</span></p>"
|
'--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, {
|
_.extend(_options, {
|
||||||
"--dont-split-on-page-breaks": true
|
'--dont-split-on-page-breaks': true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var command = [
|
var command = [
|
||||||
"ebook-convert",
|
'ebook-convert',
|
||||||
path.join(that.options.output, "SUMMARY.html"),
|
path.join(that.options.output, 'SUMMARY.html'),
|
||||||
path.join(that.options.output, "index."+that.ebookFormat),
|
path.join(that.options.output, 'index.'+that.ebookFormat),
|
||||||
stringUtils.optionsToShellArgs(_options)
|
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) {
|
if (error) {
|
||||||
that.book.log.info.fail();
|
that.book.log.info.fail();
|
||||||
|
|
||||||
if (error.code == 127) {
|
if (error.code == 127) {
|
||||||
error.message = "Need to install ebook-convert from Calibre";
|
error.message = 'Need to install ebook-convert from Calibre';
|
||||||
} else {
|
} else {
|
||||||
error.message = error.message + " "+stdout;
|
error.message = error.message + ' '+stdout;
|
||||||
}
|
}
|
||||||
return d.reject(error);
|
return d.reject(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ var Generator = function() {
|
|||||||
util.inherits(Generator, BaseGenerator);
|
util.inherits(Generator, BaseGenerator);
|
||||||
|
|
||||||
// Ignore some methods
|
// Ignore some methods
|
||||||
Generator.prototype.transferFile = function(input) { };
|
Generator.prototype.transferFile = function() { };
|
||||||
|
|
||||||
// Convert an input file
|
// Convert an input file
|
||||||
Generator.prototype.convertFile = function(input) {
|
Generator.prototype.convertFile = function(input) {
|
||||||
@@ -44,7 +44,7 @@ Generator.prototype.finish = function() {
|
|||||||
// Write README.json
|
// Write README.json
|
||||||
Generator.prototype.writeReadme = function() {
|
Generator.prototype.writeReadme = function() {
|
||||||
var that = this;
|
var that = this;
|
||||||
var mainlang, langs;
|
var mainLang, langs, readme;
|
||||||
|
|
||||||
return Q()
|
return Q()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
|
|||||||
+44
-56
@@ -1,19 +1,18 @@
|
|||||||
var util = require("util");
|
var util = require('util');
|
||||||
var path = require("path");
|
var path = require('path');
|
||||||
var Q = require("q");
|
var Q = require('q');
|
||||||
var _ = require("lodash");
|
var _ = require('lodash');
|
||||||
|
|
||||||
var nunjucks = require("nunjucks");
|
var nunjucks = require('nunjucks');
|
||||||
var AutoEscapeExtension = require("nunjucks-autoescape");
|
var AutoEscapeExtension = require('nunjucks-autoescape')(nunjucks);
|
||||||
var FilterExtension = require("nunjucks-filter");
|
var FilterExtension = require('nunjucks-filter')(nunjucks);
|
||||||
|
|
||||||
var fs = require("../utils/fs");
|
var fs = require('../utils/fs');
|
||||||
var BaseGenerator = require("../generator");
|
var BaseGenerator = require('../generator');
|
||||||
var links = require("../utils/links");
|
var links = require('../utils/links');
|
||||||
var pageUtil = require("../utils/page");
|
var i18n = require('../utils/i18n');
|
||||||
var i18n = require("../utils/i18n");
|
|
||||||
|
|
||||||
var pkg = require("../../package.json");
|
var pkg = require('../../package.json');
|
||||||
|
|
||||||
var Generator = function() {
|
var Generator = function() {
|
||||||
BaseGenerator.apply(this, arguments);
|
BaseGenerator.apply(this, arguments);
|
||||||
@@ -22,10 +21,10 @@ var Generator = function() {
|
|||||||
this.revision = new Date();
|
this.revision = new Date();
|
||||||
|
|
||||||
// Resources namespace
|
// Resources namespace
|
||||||
this.namespace = "website";
|
this.namespace = 'website';
|
||||||
|
|
||||||
// Style to integrates in the output
|
// Style to integrates in the output
|
||||||
this.styles = ["website"];
|
this.styles = ['website'];
|
||||||
|
|
||||||
// Convert images (svg -> png)
|
// Convert images (svg -> png)
|
||||||
this.convertImages = false;
|
this.convertImages = false;
|
||||||
@@ -50,12 +49,13 @@ Generator.prototype.prepareStyles = function() {
|
|||||||
this.styles = _.chain(this.styles)
|
this.styles = _.chain(this.styles)
|
||||||
.map(function(style) {
|
.map(function(style) {
|
||||||
var stylePath = that.options.styles[style];
|
var stylePath = that.options.styles[style];
|
||||||
if (fs.existsSync(path.resolve(that.book.root, stylePath))) {
|
if (stylePath && fs.existsSync(that.book.resolve(stylePath))) {
|
||||||
return stylePath;
|
return [style, stylePath];
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
.compact()
|
.compact()
|
||||||
|
.object()
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
return Q();
|
return Q();
|
||||||
@@ -63,9 +63,9 @@ Generator.prototype.prepareStyles = function() {
|
|||||||
|
|
||||||
// Prepare templates
|
// Prepare templates
|
||||||
Generator.prototype.prepareTemplates = function() {
|
Generator.prototype.prepareTemplates = function() {
|
||||||
this.templates["page"] = this.book.plugins.template("site:page") || path.resolve(this.options.theme, 'templates/website/page.html');
|
this.templates.page = this.book.plugins.template('site:page') || path.resolve(this.options.theme, 'templates/website/page.html');
|
||||||
this.templates["langs"] = this.book.plugins.template("site:langs") || path.resolve(this.options.theme, 'templates/website/langs.html');
|
this.templates.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.glossary = this.book.plugins.template('site:glossary') || path.resolve(this.options.theme, 'templates/website/glossary.html');
|
||||||
|
|
||||||
return Q();
|
return Q();
|
||||||
};
|
};
|
||||||
@@ -79,7 +79,7 @@ Generator.prototype.prepareTemplateEngine = function() {
|
|||||||
var language = that.book.config.normalizeLanguage();
|
var language = that.book.config.normalizeLanguage();
|
||||||
|
|
||||||
if (!i18n.hasLocale(language)) {
|
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)
|
var folders = _.chain(that.templates)
|
||||||
@@ -96,11 +96,10 @@ Generator.prototype.prepareTemplateEngine = function() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Add filter
|
// 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) {
|
that.env.addFilter('lvl', function(lvl) {
|
||||||
return lvl.split(".").length;
|
return lvl.split('.').length;
|
||||||
});
|
});
|
||||||
that.env.addGlobal('__', that.book.i18n.bind(that.book));
|
|
||||||
|
|
||||||
// Add extension
|
// Add extension
|
||||||
that.env.addExtension('AutoEscapeExtension', new AutoEscapeExtension(that.env));
|
that.env.addExtension('AutoEscapeExtension', new AutoEscapeExtension(that.env));
|
||||||
@@ -113,8 +112,7 @@ Generator.prototype.finish = function() {
|
|||||||
return this.copyAssets()
|
return this.copyAssets()
|
||||||
.then(this.copyCover)
|
.then(this.copyCover)
|
||||||
.then(this.writeGlossary)
|
.then(this.writeGlossary)
|
||||||
.then(this.writeSearchIndex)
|
.then(this.writeLangsIndex);
|
||||||
.then(this.writeLangsIndex)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Convert an input file
|
// Convert an input file
|
||||||
@@ -124,60 +122,48 @@ Generator.prototype.convertFile = function(input) {
|
|||||||
return that.book.parsePage(input, {
|
return that.book.parsePage(input, {
|
||||||
convertImages: that.convertImages,
|
convertImages: that.convertImages,
|
||||||
interpolateTemplate: function(page) {
|
interpolateTemplate: function(page) {
|
||||||
return that.callHook("page:before", page);
|
return that.callHook('page:before', page);
|
||||||
},
|
},
|
||||||
interpolateContent: function(page) {
|
interpolateContent: function(page) {
|
||||||
return that.callHook("page", page);
|
return that.callHook('page', page);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(function(page) {
|
.then(function(page) {
|
||||||
var relativeOutput = that.book.contentPath(page.path);
|
var relativeOutput = that.book.contentPath(page.path);
|
||||||
var output = path.join(that.options.output, relativeOutput);
|
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, '/');
|
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,
|
progress: page.progress,
|
||||||
|
|
||||||
_input: page.path,
|
_input: page.path,
|
||||||
content: page.sections,
|
content: page.sections,
|
||||||
|
|
||||||
basePath: basePath,
|
basePath: basePath,
|
||||||
staticBase: links.join(basePath, "gitbook")
|
staticBase: links.join(basePath, 'gitbook')
|
||||||
}, output);
|
}, output);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Write the index for langs
|
// Write the index for langs
|
||||||
Generator.prototype.writeLangsIndex = function() {
|
Generator.prototype.writeLangsIndex = function() {
|
||||||
var that = this;
|
|
||||||
if (!this.book.langs.length) return Q();
|
if (!this.book.langs.length) return Q();
|
||||||
return this._writeTemplate(this.templates["langs"], {
|
|
||||||
|
return this._writeTemplate(this.templates.langs, {
|
||||||
langs: this.book.langs
|
langs: this.book.langs
|
||||||
}, path.join(this.options.output, "index.html"));
|
}, path.join(this.options.output, 'index.html'));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Write glossary
|
// Write glossary
|
||||||
Generator.prototype.writeGlossary = function() {
|
Generator.prototype.writeGlossary = function() {
|
||||||
var that = this;
|
|
||||||
|
|
||||||
// No glossary
|
// No glossary
|
||||||
if (this.book.glossary.length == 0) return Q();
|
if (this.book.glossary.length === 0) return Q();
|
||||||
|
|
||||||
return this._writeTemplate(this.templates["glossary"], {}, path.join(this.options.output, "GLOSSARY.html"));
|
return this._writeTemplate(this.templates.glossary, {}, path.join(this.options.output, 'GLOSSARY.html'));
|
||||||
};
|
|
||||||
|
|
||||||
// Write the search index
|
|
||||||
Generator.prototype.writeSearchIndex = function() {
|
|
||||||
var that = this;
|
|
||||||
|
|
||||||
return fs.writeFile(
|
|
||||||
path.join(this.options.output, "search_index.json"),
|
|
||||||
JSON.stringify(this.book.searchIndex)
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Convert a page into a normalized data set
|
// Convert a page into a normalized data set
|
||||||
@@ -194,7 +180,7 @@ Generator.prototype.normalizePage = function(page) {
|
|||||||
|
|
||||||
return Q()
|
return Q()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return _callHook("page");
|
return _callHook('page');
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return page;
|
return page;
|
||||||
@@ -207,7 +193,7 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
|
|||||||
|
|
||||||
interpolate = interpolate || _.identity;
|
interpolate = interpolate || _.identity;
|
||||||
return Q()
|
return Q()
|
||||||
.then(function(sections) {
|
.then(function() {
|
||||||
return that.env.render(
|
return that.env.render(
|
||||||
tpl,
|
tpl,
|
||||||
_.extend({
|
_.extend({
|
||||||
@@ -236,8 +222,10 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
|
|||||||
|
|
||||||
options: that.options,
|
options: that.options,
|
||||||
|
|
||||||
basePath: ".",
|
basePath: '.',
|
||||||
staticBase: path.join(".", "gitbook"),
|
staticBase: path.join('.', 'gitbook'),
|
||||||
|
|
||||||
|
'__': that.book.i18n.bind(that.book)
|
||||||
}, options)
|
}, options)
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@@ -256,15 +244,15 @@ Generator.prototype.copyAssets = function() {
|
|||||||
|
|
||||||
// Copy gitbook assets
|
// Copy gitbook assets
|
||||||
return fs.copy(
|
return fs.copy(
|
||||||
path.join(that.options.theme, "assets"),
|
path.join(that.options.theme, 'assets/'+this.namespace),
|
||||||
path.join(that.options.output, "gitbook")
|
path.join(that.options.output, 'gitbook')
|
||||||
)
|
)
|
||||||
|
|
||||||
// Copy plugins assets
|
// Copy plugins assets
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return Q.all(
|
return Q.all(
|
||||||
_.map(that.book.plugins.list, function(plugin) {
|
_.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);
|
return plugin.copyAssets(pluginAssets, that.namespace);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
+57
-52
@@ -1,45 +1,50 @@
|
|||||||
var Q = require("q");
|
/*eslint no-console: 0*/
|
||||||
var _ = require("lodash");
|
|
||||||
var path = require("path");
|
var Q = require('q');
|
||||||
|
var _ = require('lodash');
|
||||||
|
var path = require('path');
|
||||||
var tinylr = require('tiny-lr');
|
var tinylr = require('tiny-lr');
|
||||||
var color = require('bash-color');
|
var color = require('bash-color');
|
||||||
|
|
||||||
var Book = require("./book");
|
var Book = require('./book');
|
||||||
var Plugin = require("./plugin");
|
var initBook = require('./init');
|
||||||
var Server = require("./utils/server");
|
var Server = require('./utils/server');
|
||||||
var stringUtils = require("./utils/string");
|
var stringUtils = require('./utils/string');
|
||||||
var watch = require("./utils/watch");
|
var watch = require('./utils/watch');
|
||||||
var logger = require("./utils/logger");
|
var logger = require('./utils/logger');
|
||||||
|
|
||||||
var LOG_OPTION = {
|
var LOG_OPTION = {
|
||||||
name: "log",
|
name: 'log',
|
||||||
description: "Minimum log level to display",
|
description: 'Minimum log level to display',
|
||||||
values: _.chain(logger.LEVELS).keys().map(stringUtils.toLowerCase).value(),
|
values: _.chain(logger.LEVELS).keys().map(stringUtils.toLowerCase).value(),
|
||||||
defaults: "info"
|
defaults: 'info'
|
||||||
};
|
};
|
||||||
|
|
||||||
var FORMAT_OPTION = {
|
var FORMAT_OPTION = {
|
||||||
name: "format",
|
name: 'format',
|
||||||
description: "Format to build to",
|
description: 'Format to build to',
|
||||||
values: ["website", "json", "ebook"],
|
values: ['website', 'json', 'ebook'],
|
||||||
defaults: "website"
|
defaults: 'website'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Export init to gitbook library
|
||||||
|
Book.init = initBook;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Book: Book,
|
Book: Book,
|
||||||
LOG_LEVELS: logger.LEVELS,
|
LOG_LEVELS: logger.LEVELS,
|
||||||
|
|
||||||
commands: _.flatten([
|
commands: _.flatten([
|
||||||
{
|
{
|
||||||
name: "build [book] [output]",
|
name: 'build [book] [output]',
|
||||||
description: "build a book",
|
description: 'build a book',
|
||||||
options: [
|
options: [
|
||||||
FORMAT_OPTION,
|
FORMAT_OPTION,
|
||||||
LOG_OPTION
|
LOG_OPTION
|
||||||
],
|
],
|
||||||
exec: function(args, kwargs) {
|
exec: function(args, kwargs) {
|
||||||
var input = args[0] || process.cwd();
|
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({}, {
|
var book = new Book(input, _.extend({}, {
|
||||||
'config': {
|
'config': {
|
||||||
@@ -53,16 +58,16 @@ module.exports = {
|
|||||||
return book.generate(kwargs.format);
|
return book.generate(kwargs.format);
|
||||||
})
|
})
|
||||||
.then(function(){
|
.then(function(){
|
||||||
console.log("");
|
console.log('');
|
||||||
console.log(color.green("Done, without error"));
|
console.log(color.green('Done, without error'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_.map(["pdf", "epub", "mobi"], function(ebookType) {
|
_.map(['pdf', 'epub', 'mobi'], function(ebookType) {
|
||||||
return {
|
return {
|
||||||
name: ebookType+" [book] [output]",
|
name: ebookType+' [book] [output]',
|
||||||
description: "build a book to "+ebookType,
|
description: 'build a book to '+ebookType,
|
||||||
options: [
|
options: [
|
||||||
LOG_OPTION
|
LOG_OPTION
|
||||||
],
|
],
|
||||||
@@ -81,30 +86,30 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(function(){
|
.then(function(){
|
||||||
console.log("");
|
console.log('');
|
||||||
console.log(color.green("Done, without error"));
|
console.log(color.green('Done, without error'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "serve [book]",
|
name: 'serve [book]',
|
||||||
description: "Build then serve a gitbook from a directory",
|
description: 'Build then serve a gitbook from a directory',
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: "port",
|
name: 'port',
|
||||||
description: "Port for server to listen on",
|
description: 'Port for server to listen on',
|
||||||
defaults: 4000
|
defaults: 4000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "lrport",
|
name: 'lrport',
|
||||||
description: "Port for livereload server to listen on",
|
description: 'Port for livereload server to listen on',
|
||||||
defaults: 35729
|
defaults: 35729
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "watch",
|
name: 'watch',
|
||||||
description: "Enable/disable file watcher",
|
description: 'Enable/disable file watcher',
|
||||||
defaults: true
|
defaults: true
|
||||||
},
|
},
|
||||||
FORMAT_OPTION,
|
FORMAT_OPTION,
|
||||||
@@ -116,16 +121,16 @@ module.exports = {
|
|||||||
|
|
||||||
// Init livereload server
|
// Init livereload server
|
||||||
var lrServer = tinylr({});
|
var lrServer = tinylr({});
|
||||||
var lrPath = undefined;
|
var lrPath;
|
||||||
|
|
||||||
var generate = function() {
|
var generate = function() {
|
||||||
if (server.isRunning()) console.log("Stopping server");
|
if (server.isRunning()) console.log('Stopping server');
|
||||||
|
|
||||||
return server.stop()
|
return server.stop()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
var book = new Book(input, _.extend({}, {
|
var book = new Book(input, _.extend({}, {
|
||||||
'config': {
|
'config': {
|
||||||
'defaultsPlugins': ["livereload"]
|
'defaultsPlugins': ['livereload']
|
||||||
},
|
},
|
||||||
'logLevel': kwargs.log
|
'logLevel': kwargs.log
|
||||||
}));
|
}));
|
||||||
@@ -158,11 +163,11 @@ module.exports = {
|
|||||||
.then(function(filepath) {
|
.then(function(filepath) {
|
||||||
// set livereload path
|
// set livereload path
|
||||||
lrPath = filepath;
|
lrPath = filepath;
|
||||||
console.log("Restart after change in files");
|
console.log('Restart after change in file', filepath);
|
||||||
console.log('');
|
console.log('');
|
||||||
return generate();
|
return generate();
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -170,16 +175,16 @@ module.exports = {
|
|||||||
.then(function() {
|
.then(function() {
|
||||||
console.log('Live reload server started on port:', kwargs.lrport);
|
console.log('Live reload server started on port:', kwargs.lrport);
|
||||||
console.log('Press CTRL+C to quit ...');
|
console.log('Press CTRL+C to quit ...');
|
||||||
console.log('')
|
console.log('');
|
||||||
return generate();
|
return generate();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "install [book]",
|
name: 'install [book]',
|
||||||
description: "install plugins dependencies",
|
description: 'install plugins dependencies',
|
||||||
exec: function(args, kwargs) {
|
exec: function(args) {
|
||||||
var input = args[0] || process.cwd();
|
var input = args[0] || process.cwd();
|
||||||
|
|
||||||
var book = new Book(input);
|
var book = new Book(input);
|
||||||
@@ -189,20 +194,20 @@ module.exports = {
|
|||||||
return book.plugins.install();
|
return book.plugins.install();
|
||||||
})
|
})
|
||||||
.then(function(){
|
.then(function(){
|
||||||
console.log("");
|
console.log('');
|
||||||
console.log(color.green("Done, without error"));
|
console.log(color.green('Done, without error'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "init [directory]",
|
name: 'init [directory]',
|
||||||
description: "create files and folders based on contents of SUMMARY.md",
|
description: 'create files and folders based on contents of SUMMARY.md',
|
||||||
exec: function(args, kwargs) {
|
exec: function(args) {
|
||||||
return Book.init(args[0] || process.cwd())
|
return initBook(args[0] || process.cwd())
|
||||||
.then(function(){
|
.then(function(){
|
||||||
console.log("");
|
console.log('');
|
||||||
console.log(color.green("Done, without error"));
|
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 _ = require('lodash');
|
||||||
var Q = require("q");
|
var Q = require('q');
|
||||||
var semver = require("semver");
|
var path = require('path');
|
||||||
var path = require("path");
|
var url = require('url');
|
||||||
var url = require("url");
|
var fs = require('./utils/fs');
|
||||||
var fs = require("./utils/fs");
|
|
||||||
var resolve = require('resolve');
|
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) {
|
var Plugin = function(book, name) {
|
||||||
this.book = book;
|
this.book = book;
|
||||||
this.name = name;
|
this.name = absoluteName(name);
|
||||||
this.packageInfos = {};
|
this.packageInfos = {};
|
||||||
this.infos = {};
|
this.infos = {};
|
||||||
|
|
||||||
@@ -18,9 +29,8 @@ var Plugin = function(book, name) {
|
|||||||
_.bindAll(this);
|
_.bindAll(this);
|
||||||
|
|
||||||
_.each([
|
_.each([
|
||||||
"gitbook-plugin-"+name,
|
absoluteName(name),
|
||||||
"gitbook-"+name,
|
name
|
||||||
name,
|
|
||||||
], function(_name) {
|
], function(_name) {
|
||||||
// Load from the book
|
// Load from the book
|
||||||
if (this.load(_name, book.root)) return false;
|
if (this.load(_name, book.root)) return false;
|
||||||
@@ -31,20 +41,27 @@ var Plugin = function(book, name) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Type of plugins resources
|
// Type of plugins resources
|
||||||
Plugin.RESOURCES = ["js", "css"];
|
Plugin.RESOURCES = ['js', 'css'];
|
||||||
Plugin.HOOKS = [
|
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
|
// Load from a name
|
||||||
Plugin.prototype.load = function(name, baseDir) {
|
Plugin.prototype.load = function(name, baseDir) {
|
||||||
try {
|
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.baseDir = path.dirname(res);
|
||||||
this.packageInfos = require(res);
|
this.packageInfos = require(res);
|
||||||
this.infos = require(resolve.sync(name, { basedir: baseDir }));
|
this.infos = require(resolve.sync(name, { basedir: baseDir }));
|
||||||
this.name = name;
|
this.name = this.packageInfos.name;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -62,13 +79,13 @@ Plugin.prototype.normalizeResource = function(resource) {
|
|||||||
// so we will simply link to using it's URL
|
// so we will simply link to using it's URL
|
||||||
if (parsed.protocol) {
|
if (parsed.protocol) {
|
||||||
return {
|
return {
|
||||||
"url": resource
|
'url': resource
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be copied over from disk
|
// This will be copied over from disk
|
||||||
// and shipped with the book's build
|
// and shipped with the book's build
|
||||||
return { "path": this.name+"/"+resource };
|
return { 'path': this.name+'/'+resource };
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return resources
|
// Return resources
|
||||||
@@ -77,7 +94,7 @@ Plugin.prototype._getResources = function(base) {
|
|||||||
var book = this.infos[base];
|
var book = this.infos[base];
|
||||||
|
|
||||||
// Compatibility with version 1.x.x
|
// 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
|
// Nothing specified, fallback to default
|
||||||
if (!book) {
|
if (!book) {
|
||||||
@@ -85,7 +102,7 @@ Plugin.prototype._getResources = function(base) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Dynamic function
|
// Dynamic function
|
||||||
if(typeof book === "function") {
|
if(typeof book === 'function') {
|
||||||
// Call giving it the context of our book
|
// Call giving it the context of our book
|
||||||
return Q().then(book.bind(this.book));
|
return Q().then(book.bind(this.book));
|
||||||
}
|
}
|
||||||
@@ -127,18 +144,49 @@ Plugin.prototype.isValid = function() {
|
|||||||
this.packageInfos.name &&
|
this.packageInfos.name &&
|
||||||
this.packageInfos.engines &&
|
this.packageInfos.engines &&
|
||||||
this.packageInfos.engines.gitbook &&
|
this.packageInfos.engines.gitbook &&
|
||||||
semver.satisfies(pkg.version, this.packageInfos.engines.gitbook)
|
version.satisfies(this.packageInfos.engines.gitbook)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Valid hooks
|
// Valid hooks
|
||||||
_.each(this.infos.hooks, function(hook, hookName) {
|
_.each(this.infos.hooks, function(hook, hookName) {
|
||||||
if (_.contains(Plugin.HOOKS, hookName)) return;
|
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;
|
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
|
// Resolve file path
|
||||||
Plugin.prototype.resolveFile = function(filename) {
|
Plugin.prototype.resolveFile = function(filename) {
|
||||||
return path.resolve(this.baseDir, filename);
|
return path.resolve(this.baseDir, filename);
|
||||||
@@ -154,8 +202,8 @@ Plugin.prototype.callHook = function(name, data) {
|
|||||||
|
|
||||||
if (!hookFunc) return Q(data);
|
if (!hookFunc) return Q(data);
|
||||||
|
|
||||||
this.book.log.debug.ln("call hook", name);
|
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");
|
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()
|
return Q()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
@@ -180,4 +228,14 @@ Plugin.prototype.copyAssets = function(out, base) {
|
|||||||
}, _.constant(false));
|
}, _.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;
|
module.exports = Plugin;
|
||||||
|
|||||||
+57
-52
@@ -1,11 +1,11 @@
|
|||||||
var _ = require("lodash");
|
var _ = require('lodash');
|
||||||
var Q = require("q");
|
var Q = require('q');
|
||||||
var npmi = require('npmi');
|
var npmi = require('npmi');
|
||||||
var npm = require('npm');
|
var npm = require('npm');
|
||||||
var semver = require('semver');
|
var semver = require('semver');
|
||||||
|
|
||||||
var Plugin = require("./plugin");
|
var Plugin = require('./plugin');
|
||||||
var pkg = require("../package.json");
|
var version = require('./version');
|
||||||
|
|
||||||
var initNPM = _.memoize(function() {
|
var initNPM = _.memoize(function() {
|
||||||
return Q.nfcall(npm.load, { silent: true, loglevel: 'silent' });
|
return Q.nfcall(npm.load, { silent: true, loglevel: 'silent' });
|
||||||
@@ -23,7 +23,7 @@ var PluginsList = function(book, plugins) {
|
|||||||
this.failed = [];
|
this.failed = [];
|
||||||
|
|
||||||
// Namespaces
|
// Namespaces
|
||||||
this.namespaces = _.chain(["website", "ebook"])
|
this.namespaces = _.chain(['website', 'ebook'])
|
||||||
.map(function(namespace) {
|
.map(function(namespace) {
|
||||||
return [
|
return [
|
||||||
namespace,
|
namespace,
|
||||||
@@ -53,7 +53,7 @@ PluginsList.prototype.count = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Add and load a plugin
|
// Add and load a plugin
|
||||||
PluginsList.prototype.load = function(plugin, options) {
|
PluginsList.prototype.load = function(plugin) {
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
if (_.isArray(plugin)) {
|
if (_.isArray(plugin)) {
|
||||||
@@ -66,7 +66,7 @@ PluginsList.prototype.load = function(plugin, options) {
|
|||||||
if (_.isObject(plugin) && !(plugin instanceof Plugin)) plugin = plugin.name;
|
if (_.isObject(plugin) && !(plugin instanceof Plugin)) plugin = plugin.name;
|
||||||
if (_.isString(plugin)) plugin = new Plugin(this.book, plugin);
|
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()) {
|
if (!plugin.isValid()) {
|
||||||
that.log.info.fail();
|
that.log.info.fail();
|
||||||
that.failed.push(plugin.name);
|
that.failed.push(plugin.name);
|
||||||
@@ -78,38 +78,46 @@ PluginsList.prototype.load = function(plugin, options) {
|
|||||||
that.list.push(plugin);
|
that.list.push(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract filters
|
return Q()
|
||||||
_.each(plugin.getFilters(), function(filterFunc, filterName) {
|
|
||||||
that.book.template.addFilter(filterName, filterFunc);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Extract blocks
|
// Validate and normalize configuration
|
||||||
_.each(plugin.getBlocks(), function(block, blockName) {
|
.then(function() {
|
||||||
that.book.template.addBlock(blockName, block);
|
var config = plugin.getConfig();
|
||||||
});
|
return plugin.validateConfig(config);
|
||||||
|
})
|
||||||
|
.then(function(config) {
|
||||||
|
// Update configuration
|
||||||
|
plugin.setConfig(config);
|
||||||
|
|
||||||
return _.reduce(_.keys(that.namespaces), function(prev, namespaceName) {
|
// Extract filters
|
||||||
return prev.then(function() {
|
that.book.template.addFilters(plugin.getFilters());
|
||||||
return plugin.getResources(namespaceName)
|
|
||||||
.then(function(plResources) {
|
|
||||||
var namespace = that.namespaces[namespaceName];
|
|
||||||
|
|
||||||
// Extract js and css
|
// Extract blocks
|
||||||
_.each(Plugin.RESOURCES, function(resourceType) {
|
that.book.template.addBlocks(plugin.getBlocks());
|
||||||
namespace.resources[resourceType] = (namespace.resources[resourceType] || []).concat(plResources[resourceType] || []);
|
|
||||||
|
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
|
}, Q());
|
||||||
_.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());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Call a hook
|
// Call a hook
|
||||||
@@ -139,7 +147,7 @@ PluginsList.prototype.html = function(namespace, tag, context, options) {
|
|||||||
var htmlSnippets = this.namespaces[namespace].html[tag];
|
var htmlSnippets = this.namespaces[namespace].html[tag];
|
||||||
return _.map(htmlSnippets || [], function(code) {
|
return _.map(htmlSnippets || [], function(code) {
|
||||||
return code.call(context, options);
|
return code.call(context, options);
|
||||||
}).join("\n");
|
}).join('\n');
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return a resources map for a namespace
|
// Return a resources map for a namespace
|
||||||
@@ -150,18 +158,17 @@ PluginsList.prototype.resources = function(namespace) {
|
|||||||
// Install plugins from a book
|
// Install plugins from a book
|
||||||
PluginsList.prototype.install = function() {
|
PluginsList.prototype.install = function() {
|
||||||
var that = this;
|
var that = this;
|
||||||
var defaultsPlugins = _.pluck(that.book.options.defaultsPlugins)
|
|
||||||
|
|
||||||
// Remove defaults (no need to install)
|
// Remove defaults (no need to install)
|
||||||
var plugins = _.filter(that.book.options.plugins, function(plugin) {
|
var plugins = _.reject(that.book.options.plugins, {
|
||||||
return !_.contains(defaultsPlugins, plugin.name);
|
isDefault: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// Install plugins one by one
|
// 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 _.reduce(plugins, function(prev, plugin) {
|
||||||
return prev.then(function() {
|
return prev.then(function() {
|
||||||
var fullname = "gitbook-plugin-"+plugin.name;
|
var fullname = 'gitbook-plugin-'+plugin.name;
|
||||||
|
|
||||||
return Q()
|
return Q()
|
||||||
|
|
||||||
@@ -169,10 +176,10 @@ PluginsList.prototype.install = function() {
|
|||||||
.then(function() {
|
.then(function() {
|
||||||
if (plugin.version) return plugin.version;
|
if (plugin.version) return plugin.version;
|
||||||
|
|
||||||
that.book.log.info.ln("No version specified, resolve plugin", plugin.name);
|
that.book.log.info.ln('No version specified, resolve plugin', plugin.name);
|
||||||
return initNPM()
|
return initNPM()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return Q.nfcall(npm.commands.view, [fullname+"@*", "engines"], true);
|
return Q.nfcall(npm.commands.view, [fullname+'@*', 'engines'], true);
|
||||||
})
|
})
|
||||||
.then(function(versions) {
|
.then(function(versions) {
|
||||||
return _.chain(versions)
|
return _.chain(versions)
|
||||||
@@ -180,16 +187,16 @@ PluginsList.prototype.install = function() {
|
|||||||
.map(function(v) {
|
.map(function(v) {
|
||||||
return {
|
return {
|
||||||
version: v[0],
|
version: v[0],
|
||||||
gitbook: (v[1].engines || {})["gitbook"]
|
gitbook: (v[1].engines || {}).gitbook
|
||||||
}
|
};
|
||||||
})
|
})
|
||||||
.filter(function(v) {
|
.filter(function(v) {
|
||||||
return v.gitbook && semver.satisfies(pkg.version, v.gitbook);
|
return v.gitbook && version.satisfies(v.gitbook);
|
||||||
})
|
})
|
||||||
.sort(function(v1, v2) {
|
.sort(function(v1, v2) {
|
||||||
return semver.lt(v1.version, v2.version)? 1 : -1;
|
return semver.lt(v1.version, v2.version)? 1 : -1;
|
||||||
})
|
})
|
||||||
.pluck("version")
|
.pluck('version')
|
||||||
.first()
|
.first()
|
||||||
.value();
|
.value();
|
||||||
});
|
});
|
||||||
@@ -198,10 +205,10 @@ PluginsList.prototype.install = function() {
|
|||||||
// Install the plugin with the resolved version
|
// Install the plugin with the resolved version
|
||||||
.then(function(version) {
|
.then(function(version) {
|
||||||
if (!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, {
|
return Q.nfcall(npmi, {
|
||||||
'name': fullname,
|
'name': fullname,
|
||||||
'version': version,
|
'version': version,
|
||||||
@@ -214,12 +221,10 @@ PluginsList.prototype.install = function() {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(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());
|
}, Q());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = PluginsList;
|
module.exports = PluginsList;
|
||||||
|
|||||||
+160
-94
@@ -1,57 +1,42 @@
|
|||||||
var _ = require("lodash");
|
var _ = require('lodash');
|
||||||
var Q = require("q");
|
var Q = require('q');
|
||||||
var path = require("path");
|
var path = require('path');
|
||||||
var nunjucks = require("nunjucks");
|
var nunjucks = require('nunjucks');
|
||||||
var escapeStringRegexp = require("escape-string-regexp");
|
var parsers = require('gitbook-parsers');
|
||||||
|
var escapeStringRegexp = require('escape-string-regexp');
|
||||||
|
|
||||||
var git = require("./utils/git");
|
var batch = require('./utils/batch');
|
||||||
var fs = require("./utils/fs");
|
var pkg = require('../package.json');
|
||||||
var batch = require("./utils/batch");
|
var defaultBlocks = require('./blocks');
|
||||||
var pkg = require("../package.json");
|
var BookLoader = require('./conrefs_loader');
|
||||||
|
|
||||||
|
// Normalize result from a block
|
||||||
// The loader should handle relative and git url
|
function normBlockResult(blk) {
|
||||||
var BookLoader = nunjucks.Loader.extend({
|
if (_.isString(blk)) blk = { body: blk };
|
||||||
async: true,
|
return blk;
|
||||||
|
}
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
var TemplateEngine = function(book) {
|
var TemplateEngine = function(book) {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
this.book = book;
|
this.book = book;
|
||||||
this.log = this.book.log;
|
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
|
// Nunjucks env
|
||||||
this.env = new nunjucks.Environment(
|
this.env = new nunjucks.Environment(
|
||||||
new BookLoader(book),
|
this.loader,
|
||||||
{
|
{
|
||||||
// Escaping is done after by the markdown parser
|
// Escaping is done after by the markdown parser
|
||||||
autoescape: false,
|
autoescape: false,
|
||||||
@@ -71,49 +56,48 @@ var TemplateEngine = function(book) {
|
|||||||
// List of tags shortcuts
|
// List of tags shortcuts
|
||||||
this.shortcuts = [];
|
this.shortcuts = [];
|
||||||
|
|
||||||
// Map of blocks
|
// Map of blocks bodies (that requires post-processing)
|
||||||
|
this.blockBodies = {};
|
||||||
|
|
||||||
|
// Map of added blocks
|
||||||
this.blocks = {};
|
this.blocks = {};
|
||||||
|
|
||||||
// Bind methods
|
// Bind methods
|
||||||
_.bindAll(this);
|
_.bindAll(this);
|
||||||
|
|
||||||
// Default block "html" that return html not parsed
|
// Add default blocks
|
||||||
this.addBlock("html", {
|
this.addBlocks(defaultBlocks);
|
||||||
process: _.identity
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Process a block in a context
|
// Process the result of block in a context
|
||||||
TemplateEngine.prototype.processBlock = function(blk) {
|
TemplateEngine.prototype.processBlock = function(blk) {
|
||||||
if (_.isString(blk)) blk = { body: blk };
|
|
||||||
|
|
||||||
blk = _.defaults(blk, {
|
blk = _.defaults(blk, {
|
||||||
parse: false,
|
parse: false,
|
||||||
post: undefined
|
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
|
// 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) {
|
if (blk.parse) {
|
||||||
return blk.body;
|
return blk.body;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return it as a macro
|
// Return it as a position marker
|
||||||
return "%+%"+blk.id+"%+%";
|
return '@%@'+blk.id+'@%@';
|
||||||
};
|
};
|
||||||
|
|
||||||
// Replace blocks by body after processing
|
// Replace position markers of blocks by body after processing
|
||||||
// This is done to avoid that markdown processer parse the block content
|
// This is done to avoid that markdown/asciidoc processer parse the block content
|
||||||
TemplateEngine.prototype.replaceBlocks = function(content) {
|
TemplateEngine.prototype.replaceBlocks = function(content) {
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
return content.replace(/\%\+\%([\s\S]+?)\%\+\%/g, function(match, key) {
|
return content.replace(/\@\%\@([\s\S]+?)\@\%\@/g, function(match, key) {
|
||||||
var blk = that.blocks[key];
|
var blk = that.blockBodies[key];
|
||||||
if (!blk) return match;
|
if (!blk) return match;
|
||||||
|
|
||||||
var body = blk.body;
|
var body = blk.body;
|
||||||
@@ -141,11 +125,13 @@ TemplateEngine.prototype.bindContext = function(func) {
|
|||||||
TemplateEngine.prototype.addFilter = function(filterName, func) {
|
TemplateEngine.prototype.addFilter = function(filterName, func) {
|
||||||
try {
|
try {
|
||||||
this.env.getFilter(filterName);
|
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;
|
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() {
|
this.env.addFilter(filterName, this.bindContext(function() {
|
||||||
var ctx = this;
|
var ctx = this;
|
||||||
var args = Array.prototype.slice.apply(arguments);
|
var args = Array.prototype.slice.apply(arguments);
|
||||||
@@ -160,29 +146,65 @@ TemplateEngine.prototype.addFilter = function(filterName, func) {
|
|||||||
return true;
|
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
|
// Add a block
|
||||||
TemplateEngine.prototype.addBlock = function(name, block) {
|
TemplateEngine.prototype.addBlock = function(name, block) {
|
||||||
var that = this;
|
var that = this, Ext, extName;
|
||||||
|
|
||||||
|
if (_.isFunction(block)) block = { process: block };
|
||||||
|
|
||||||
block = _.defaults(block || {}, {
|
block = _.defaults(block || {}, {
|
||||||
shortcuts: [],
|
shortcuts: [],
|
||||||
end: "end"+name,
|
end: 'end'+name,
|
||||||
process: _.identity,
|
process: _.identity,
|
||||||
blocks: []
|
blocks: []
|
||||||
});
|
});
|
||||||
|
|
||||||
var extName = 'Block'+name+'Extension';
|
extName = this.blockExtName(name);
|
||||||
if (this.env.getExtension(extName)) {
|
|
||||||
this.log.warn.ln("conflict in blocks, '"+name+"' is already defined");
|
if (this.hasBlock(name) && !defaultBlocks[name]) {
|
||||||
return false;
|
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.tags = [name];
|
||||||
|
|
||||||
this.parse = function(parser, nodes, lexer) {
|
this.parse = function(parser, nodes) {
|
||||||
var body = null;
|
var body = null;
|
||||||
var lastBlockName = null;
|
var lastBlockName = null;
|
||||||
var lastBlockArgs = null;
|
var lastBlockArgs = null;
|
||||||
@@ -223,7 +245,7 @@ TemplateEngine.prototype.addBlock = function(name, block) {
|
|||||||
var bodies = [body];
|
var bodies = [body];
|
||||||
_.each(block.blocks, function(blockName) {
|
_.each(block.blocks, function(blockName) {
|
||||||
subbodies[blockName] = subbodies[blockName] || [];
|
subbodies[blockName] = subbodies[blockName] || [];
|
||||||
if (subbodies[blockName].length == 0) {
|
if (subbodies[blockName].length === 0) {
|
||||||
subbodies[blockName].push({
|
subbodies[blockName].push({
|
||||||
args: new nodes.NodeList(),
|
args: new nodes.NodeList(),
|
||||||
body: new nodes.NodeList()
|
body: new nodes.NodeList()
|
||||||
@@ -250,7 +272,7 @@ TemplateEngine.prototype.addBlock = function(name, block) {
|
|||||||
|
|
||||||
// Extract main body and kwargs
|
// Extract main body and kwargs
|
||||||
var body = args.pop();
|
var body = args.pop();
|
||||||
var kwargs = args.pop() || {};
|
var kwargs = _.isObject(_.last(args))? args.pop() : {};
|
||||||
|
|
||||||
// Extract blocks body
|
// Extract blocks body
|
||||||
var _blocks = _.map(block.blocks, function(blockName, i){
|
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()
|
Q()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return func.call(context, {
|
return that.applyBlock(name, {
|
||||||
body: body(),
|
body: body(),
|
||||||
args: args,
|
args: args,
|
||||||
kwargs: kwargs,
|
kwargs: kwargs,
|
||||||
blocks: _blocks
|
blocks: _blocks
|
||||||
});
|
}, context);
|
||||||
})
|
})
|
||||||
|
|
||||||
// process the block returned
|
// process the block returned
|
||||||
.then(that.processBlock)
|
.then(that.processBlock)
|
||||||
.nodeify(callback)
|
.nodeify(callback);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Add the Extension
|
// Add the Extension
|
||||||
this.env.addExtension(extName, new Ext());
|
this.env.addExtension(extName, new Ext());
|
||||||
|
|
||||||
// Add shortcuts
|
// Add shortcuts
|
||||||
if (!_.isArray(block.shortcuts)) block.shortcuts = [block.shortcuts];
|
if (!_.isArray(block.shortcuts)) block.shortcuts = [block.shortcuts];
|
||||||
_.each(block.shortcuts, function(shortcut) {
|
_.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({
|
this.shortcuts.push({
|
||||||
|
block: name,
|
||||||
parsers: shortcut.parsers,
|
parsers: shortcut.parsers,
|
||||||
start: shortcut.start,
|
start: shortcut.start,
|
||||||
end: shortcut.end,
|
end: shortcut.end,
|
||||||
@@ -298,24 +318,66 @@ TemplateEngine.prototype.addBlock = function(name, block) {
|
|||||||
}, this);
|
}, 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
|
// Apply a shortcut to a string
|
||||||
TemplateEngine.prototype._applyShortcut = function(parser, content, shortcut) {
|
TemplateEngine.prototype._applyShortcut = function(parser, content, shortcut) {
|
||||||
if (!_.contains(shortcut.parsers, parser)) return content;
|
if (!_.contains(shortcut.parsers, parser)) return content;
|
||||||
var regex = new RegExp(
|
var regex = new RegExp(
|
||||||
escapeStringRegexp(shortcut.start) + "([\\s\\S]*?[^\\$])" + escapeStringRegexp(shortcut.end),
|
escapeStringRegexp(shortcut.start) + '([\\s\\S]*?[^\\$])' + escapeStringRegexp(shortcut.end),
|
||||||
'g'
|
'g'
|
||||||
);
|
);
|
||||||
return content.replace(regex, function(all, match) {
|
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
|
// Render a string from the book
|
||||||
TemplateEngine.prototype.renderString = function(content, context, options) {
|
TemplateEngine.prototype.renderString = function(content, context, options) {
|
||||||
var context = _.extend({}, context, {
|
context = _.extend({}, context, {
|
||||||
// Variables from book.json
|
// Variables from book.json
|
||||||
book: this.book.options.variables,
|
book: this.book.options.variables,
|
||||||
|
|
||||||
|
// Complete book.json
|
||||||
|
config: this.book.options,
|
||||||
|
|
||||||
// infos about gitbook
|
// infos about gitbook
|
||||||
gitbook: {
|
gitbook: {
|
||||||
version: pkg.version,
|
version: pkg.version,
|
||||||
@@ -327,22 +389,26 @@ TemplateEngine.prototype.renderString = function(content, context, options) {
|
|||||||
type: null
|
type: null
|
||||||
});
|
});
|
||||||
if (options.path) options.path = this.book.resolve(options.path);
|
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
|
// 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)
|
return Q.nfcall(this.env.renderString.bind(this.env), content, context, options)
|
||||||
.fail(function(err) {
|
.fail(function(err) {
|
||||||
if (_.isString(err)) err = new Error(err);
|
if (_.isString(err)) err = new Error(err);
|
||||||
err.message = err.message.replace(/^Error: /, "");
|
err.message = err.message.replace(/^Error: /, '');
|
||||||
|
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render a file from the book
|
// Render a file from the book
|
||||||
TemplateEngine.prototype.renderFile = function(filename, options) {
|
TemplateEngine.prototype.renderFile = function(filename) {
|
||||||
var that = this, context;
|
var that = this;
|
||||||
|
|
||||||
return that.book.readFile(filename)
|
return that.book.readFile(filename)
|
||||||
.then(function(content) {
|
.then(function(content) {
|
||||||
@@ -354,11 +420,11 @@ TemplateEngine.prototype.renderFile = function(filename, options) {
|
|||||||
|
|
||||||
// Render a page from the book
|
// Render a page from the book
|
||||||
TemplateEngine.prototype.renderPage = function(page) {
|
TemplateEngine.prototype.renderPage = function(page) {
|
||||||
var that = this, context;
|
var that = this;
|
||||||
|
|
||||||
return that.book.statFile(page.path)
|
return that.book.statFile(page.path)
|
||||||
.then(function(stat) {
|
.then(function(stat) {
|
||||||
context = {
|
var context = {
|
||||||
// infos about the file
|
// infos about the file
|
||||||
file: {
|
file: {
|
||||||
path: page.path,
|
path: page.path,
|
||||||
@@ -380,7 +446,7 @@ TemplateEngine.prototype.postProcess = function(content) {
|
|||||||
return Q(content)
|
return Q(content)
|
||||||
.then(that.replaceBlocks)
|
.then(that.replaceBlocks)
|
||||||
.then(function(_content) {
|
.then(function(_content) {
|
||||||
return batch.execEach(that.blocks, {
|
return batch.execEach(that.blockBodies, {
|
||||||
max: 20,
|
max: 20,
|
||||||
fn: function(blk, blkId) {
|
fn: function(blk, blkId) {
|
||||||
return Q()
|
return Q()
|
||||||
@@ -389,7 +455,7 @@ TemplateEngine.prototype.postProcess = function(content) {
|
|||||||
return blk.post();
|
return blk.post();
|
||||||
})
|
})
|
||||||
.then(function() {
|
.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
|
// Execute a method for all element
|
||||||
function execEach(items, options) {
|
function execEach(items, options) {
|
||||||
if (_.size(items) == 0) return Q();
|
if (_.size(items) === 0) return Q();
|
||||||
var concurrents = 0, d = Q.defer(), pending = [];
|
var concurrents = 0, d = Q.defer(), pending = [];
|
||||||
|
|
||||||
options = _.defaults(options || {}, {
|
options = _.defaults(options || {}, {
|
||||||
max: 100,
|
max: 100,
|
||||||
fn: function(item) {}
|
fn: function() {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ function execEach(items, options) {
|
|||||||
// Next pending
|
// Next pending
|
||||||
var next = pending.shift();
|
var next = pending.shift();
|
||||||
|
|
||||||
if (concurrents == 0 && !next) {
|
if (concurrents === 0 && !next) {
|
||||||
d.resolve();
|
d.resolve();
|
||||||
} else if (next) {
|
} else if (next) {
|
||||||
startItem.apply(null, next);
|
startItem.apply(null, next);
|
||||||
@@ -38,7 +38,7 @@ function execEach(items, options) {
|
|||||||
.fail(function(err) {
|
.fail(function(err) {
|
||||||
pending = [];
|
pending = [];
|
||||||
d.reject(err);
|
d.reject(err);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_.each(items, startItem);
|
_.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 _ = require('lodash');
|
||||||
var Q = require("q");
|
var Q = require('q');
|
||||||
var tmp = require("tmp");
|
var tmp = require('tmp');
|
||||||
var path = require("path");
|
var path = require('path');
|
||||||
var fs = require("graceful-fs");
|
var fs = require('graceful-fs');
|
||||||
var fsExtra = require("fs-extra");
|
var fsExtra = require('fs-extra');
|
||||||
var Ignore = require("fstream-ignore");
|
var Ignore = require('fstream-ignore');
|
||||||
|
|
||||||
var fsUtils = {
|
var fsUtils = {
|
||||||
tmp: {
|
tmp: {
|
||||||
file: function(opt) {
|
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() {
|
dir: function() {
|
||||||
return Q.nfcall(tmp.dir.bind(tmp)).get(0)
|
return Q.nfcall(tmp.dir.bind(tmp)).get(0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
list: listFiles,
|
list: listFiles,
|
||||||
@@ -33,15 +33,15 @@ var fsUtils = {
|
|||||||
existsSync: fs.existsSync.bind(fs),
|
existsSync: fs.existsSync.bind(fs),
|
||||||
readFileSync: fs.readFileSync.bind(fs),
|
readFileSync: fs.readFileSync.bind(fs),
|
||||||
clean: cleanFolder,
|
clean: cleanFolder,
|
||||||
getUniqueFilename: getUniqueFilename,
|
getUniqueFilename: getUniqueFilename
|
||||||
}
|
};
|
||||||
|
|
||||||
// Write a file
|
// Write a file
|
||||||
function writeFile(filename, data, options) {
|
function writeFile(filename, data, options) {
|
||||||
var d = Q.defer();
|
var d = Q.defer();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(filename, data, options)
|
fs.writeFileSync(filename, data, options);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
d.reject(err);
|
d.reject(err);
|
||||||
}
|
}
|
||||||
@@ -64,6 +64,10 @@ function writeStream(filename, st) {
|
|||||||
d.reject(err);
|
d.reject(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
st.on('error', function(err) {
|
||||||
|
d.reject(err);
|
||||||
|
});
|
||||||
|
|
||||||
st.pipe(wstream);
|
st.pipe(wstream);
|
||||||
|
|
||||||
return d.promise;
|
return d.promise;
|
||||||
@@ -73,7 +77,7 @@ function writeStream(filename, st) {
|
|||||||
function getUniqueFilename(base, filename) {
|
function getUniqueFilename(base, filename) {
|
||||||
if (!filename) {
|
if (!filename) {
|
||||||
filename = base;
|
filename = base;
|
||||||
base = "/";
|
base = '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
filename = path.resolve(base, filename);
|
filename = path.resolve(base, filename);
|
||||||
@@ -83,9 +87,8 @@ function getUniqueFilename(base, filename) {
|
|||||||
var _filename = filename+ext;
|
var _filename = filename+ext;
|
||||||
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
while (1) {
|
while (fs.existsSync(filename)) {
|
||||||
if (!fs.existsSync(filename)) break;
|
_filename = filename+'_'+i+ext;
|
||||||
_filename = filename+"_"+i+ext;
|
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+21
-6
@@ -1,11 +1,10 @@
|
|||||||
var Q = require("q");
|
var Q = require("q");
|
||||||
var _ = require("lodash");
|
var _ = require("lodash");
|
||||||
var url = require("url");
|
|
||||||
var tmp = require("tmp");
|
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
var crc = require("crc");
|
var crc = require("crc");
|
||||||
var exec = Q.denodeify(require("child_process").exec);
|
var exec = Q.denodeify(require("child_process").exec);
|
||||||
var URI = require("URIjs");
|
var URI = require("URIjs");
|
||||||
|
var pathUtil = require("./path");
|
||||||
|
|
||||||
var fs = require("./fs");
|
var fs = require("./fs");
|
||||||
|
|
||||||
@@ -83,13 +82,12 @@ function cloneGitRepo(host, ref) {
|
|||||||
return exec("git clone "+host+" "+repoPath)
|
return exec("git clone "+host+" "+repoPath)
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return exec("git checkout "+ref, { cwd: repoPath });
|
return exec("git checkout "+ref, { cwd: repoPath });
|
||||||
})
|
});
|
||||||
})
|
})
|
||||||
.thenResolve(repoPath);
|
.thenResolve(repoPath);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Get file from a git repo
|
// Get file from a git repo
|
||||||
function resolveFileFromGit(giturl) {
|
function resolveFileFromGit(giturl) {
|
||||||
if (_.isString(giturl)) giturl = parseGitUrl(giturl);
|
if (_.isString(giturl)) giturl = parseGitUrl(giturl);
|
||||||
@@ -102,11 +100,28 @@ function resolveFileFromGit(giturl) {
|
|||||||
// Resolve relative path
|
// Resolve relative path
|
||||||
return path.resolve(repo, giturl.filepath);
|
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 = {
|
module.exports = {
|
||||||
checkUrl: checkGitUrl,
|
checkUrl: checkGitUrl,
|
||||||
parseUrl: parseGitUrl,
|
parseUrl: parseGitUrl,
|
||||||
resolveFile: resolveFileFromGit
|
resolveFile: resolveFileFromGit,
|
||||||
|
resolveRoot: resolveGitRoot
|
||||||
};
|
};
|
||||||
|
|||||||
+11
-11
@@ -4,7 +4,7 @@ var fs = require("fs");
|
|||||||
|
|
||||||
var i18n = require("i18n");
|
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 DEFAULT_LANGUAGE = "en";
|
||||||
var LOCALES = _.map(fs.readdirSync(I18N_PATH), function(lang) {
|
var LOCALES = _.map(fs.readdirSync(I18N_PATH), function(lang) {
|
||||||
return path.basename(lang, ".json");
|
return path.basename(lang, ".json");
|
||||||
@@ -17,7 +17,7 @@ i18n.configure({
|
|||||||
updateFiles: false
|
updateFiles: false
|
||||||
});
|
});
|
||||||
|
|
||||||
var compareLocales = function(lang, locale) {
|
function compareLocales(lang, locale) {
|
||||||
var langMain = _.first(lang.split("-"));
|
var langMain = _.first(lang.split("-"));
|
||||||
var langSecond = _.last(lang.split("-"));
|
var langSecond = _.last(lang.split("-"));
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ var compareLocales = function(lang, locale) {
|
|||||||
if (localeMain == langMain) return 50;
|
if (localeMain == langMain) return 50;
|
||||||
if (localeSecond == langSecond) return 20;
|
if (localeSecond == langSecond) return 20;
|
||||||
return 0;
|
return 0;
|
||||||
};
|
}
|
||||||
|
|
||||||
var normalizeLanguage = _.memoize(function(lang) {
|
var normalizeLanguage = _.memoize(function(lang) {
|
||||||
var language = _.chain(LOCALES)
|
var language = _.chain(LOCALES)
|
||||||
@@ -37,7 +37,7 @@ var normalizeLanguage = _.memoize(function(lang) {
|
|||||||
return {
|
return {
|
||||||
locale: locale,
|
locale: locale,
|
||||||
score: compareLocales(lang, locale)
|
score: compareLocales(lang, locale)
|
||||||
}
|
};
|
||||||
})
|
})
|
||||||
.filter(function(lang) {
|
.filter(function(lang) {
|
||||||
return lang.score > 0;
|
return lang.score > 0;
|
||||||
@@ -49,25 +49,25 @@ var normalizeLanguage = _.memoize(function(lang) {
|
|||||||
return language || lang;
|
return language || lang;
|
||||||
});
|
});
|
||||||
|
|
||||||
var translate = function(locale, phrase) {
|
function translate(locale, phrase) {
|
||||||
var args = Array.prototype.slice.call(arguments, 2);
|
var args = Array.prototype.slice.call(arguments, 2);
|
||||||
|
|
||||||
return i18n.__.apply({}, [{
|
return i18n.__.apply({}, [{
|
||||||
locale: locale,
|
locale: locale,
|
||||||
phrase: phrase
|
phrase: phrase
|
||||||
}].concat(args));
|
}].concat(args));
|
||||||
};
|
}
|
||||||
|
|
||||||
var getCatalog = function(locale) {
|
function getCatalog(locale) {
|
||||||
locale = normalizeLanguage(locale);
|
locale = normalizeLanguage(locale);
|
||||||
return i18n.getCatalog(locale);
|
return i18n.getCatalog(locale);
|
||||||
};
|
}
|
||||||
|
|
||||||
var getLocales = function() {
|
function getLocales() {
|
||||||
return LOCALES;
|
return LOCALES;
|
||||||
};
|
}
|
||||||
|
|
||||||
var hasLocale = function(locale) {
|
function hasLocale(locale) {
|
||||||
return _.contains(LOCALES, locale);
|
return _.contains(LOCALES, locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-7
@@ -1,9 +1,7 @@
|
|||||||
var _ = require("lodash");
|
var _ = require("lodash");
|
||||||
var Q = require("q");
|
var Q = require("q");
|
||||||
var fs = require("./fs");
|
var fs = require("./fs");
|
||||||
var spawn = require('spawn-cmd').spawn;
|
var spawn = require("spawn-cmd").spawn;
|
||||||
|
|
||||||
var links = require("./links");
|
|
||||||
|
|
||||||
// Convert a svg file
|
// Convert a svg file
|
||||||
var convertSVG = function(source, dest, options) {
|
var convertSVG = function(source, dest, options) {
|
||||||
@@ -14,16 +12,16 @@ var convertSVG = function(source, dest, options) {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//var command = shellescape(['svgexport', source, dest]);
|
//var command = shellescape(["svgexport", source, dest]);
|
||||||
var child = spawn('svgexport', [source, dest]);
|
var child = spawn("svgexport", [source, dest]);
|
||||||
|
|
||||||
child.on("error", function(error) {
|
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);
|
return d.reject(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
child.on("close", function(code) {
|
child.on("close", function(code) {
|
||||||
if (code == 0 && fs.existsSync(dest)) {
|
if (code === 0 && fs.existsSync(dest)) {
|
||||||
d.resolve();
|
d.resolve();
|
||||||
} else {
|
} else {
|
||||||
d.reject(new Error("Error converting "+source+" into "+dest));
|
d.reject(new Error("Error converting "+source+" into "+dest));
|
||||||
|
|||||||
+4
-4
@@ -1,5 +1,5 @@
|
|||||||
var url = require('url');
|
var url = require("url");
|
||||||
var path = require('path');
|
var path = require("path");
|
||||||
|
|
||||||
// Is the link an external link
|
// Is the link an external link
|
||||||
var isExternal = function(href) {
|
var isExternal = function(href) {
|
||||||
@@ -33,7 +33,7 @@ var isAnchor = function(href) {
|
|||||||
|
|
||||||
// Normalize a path to be a link
|
// Normalize a path to be a link
|
||||||
var normalizeLink = function(s) {
|
var normalizeLink = function(s) {
|
||||||
return s.replace(/\\/g, '/');
|
return s.replace(/\\/g, "/");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Relative to absolute path
|
// Relative to absolute path
|
||||||
@@ -43,7 +43,7 @@ var normalizeLink = function(s) {
|
|||||||
var toAbsolute = function(_href, dir, outdir) {
|
var toAbsolute = function(_href, dir, outdir) {
|
||||||
if (isExternal(_href)) return _href;
|
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));
|
var hrefInRoot = path.normalize(path.join(dir, _href));
|
||||||
if (_href[0] == "/") hrefInRoot = path.normalize(_href.slice(1));
|
if (_href[0] == "/") hrefInRoot = path.normalize(_href.slice(1));
|
||||||
|
|
||||||
|
|||||||
+9
-9
@@ -1,6 +1,6 @@
|
|||||||
var _ = require('lodash');
|
var _ = require("lodash");
|
||||||
var util = require('util');
|
var util = require("util");
|
||||||
var color = require('bash-color');
|
var color = require("bash-color");
|
||||||
|
|
||||||
var LEVELS = {
|
var LEVELS = {
|
||||||
DEBUG: 0,
|
DEBUG: 0,
|
||||||
@@ -19,7 +19,7 @@ var COLORS = {
|
|||||||
|
|
||||||
module.exports = function(_write, logLevel) {
|
module.exports = function(_write, logLevel) {
|
||||||
var logger = {};
|
var logger = {};
|
||||||
var lastChar = '\n';
|
var lastChar = "\n";
|
||||||
if (_.isString(logLevel)) logLevel = LEVELS[logLevel.toUpperCase()];
|
if (_.isString(logLevel)) logLevel = LEVELS[logLevel.toUpperCase()];
|
||||||
|
|
||||||
// Write a simple message
|
// Write a simple message
|
||||||
@@ -47,14 +47,14 @@ module.exports = function(_write, logLevel) {
|
|||||||
var args = Array.prototype.slice.apply(arguments, [1]);
|
var args = Array.prototype.slice.apply(arguments, [1]);
|
||||||
var msg = logger.format.apply(logger, args);
|
var msg = logger.format.apply(logger, args);
|
||||||
|
|
||||||
if (lastChar == '\n') {
|
if (lastChar == "\n") {
|
||||||
msg = COLORS[levelKey](levelKey.toLowerCase()+":")+" "+msg;
|
msg = COLORS[levelKey](levelKey.toLowerCase()+":")+" "+msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
return logger.write(msg);
|
return logger.write(msg);
|
||||||
};
|
};
|
||||||
logger.logLn = function() {
|
logger.logLn = function() {
|
||||||
if (lastChar != '\n') logger.write("\n");
|
if (lastChar != "\n") logger.write("\n");
|
||||||
|
|
||||||
var args = Array.prototype.slice.apply(arguments);
|
var args = Array.prototype.slice.apply(arguments);
|
||||||
args.push("\n");
|
args.push("\n");
|
||||||
@@ -66,7 +66,7 @@ module.exports = function(_write, logLevel) {
|
|||||||
var args = Array.prototype.slice.apply(arguments, [1]);
|
var args = Array.prototype.slice.apply(arguments, [1]);
|
||||||
var msg = logger.format.apply(logger, args);
|
var msg = logger.format.apply(logger, args);
|
||||||
if (arguments.length > 1) {
|
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 {
|
} else {
|
||||||
logger.log(level, color.green("OK"), "\n");
|
logger.log(level, color.green("OK"), "\n");
|
||||||
}
|
}
|
||||||
@@ -77,7 +77,7 @@ module.exports = function(_write, logLevel) {
|
|||||||
return logger.log(level, color.red("ERROR")+"\n");
|
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();
|
levelKey = levelKey.toLowerCase();
|
||||||
|
|
||||||
logger[levelKey] = _.partial(logger.log, level);
|
logger[levelKey] = _.partial(logger.log, level);
|
||||||
@@ -93,7 +93,7 @@ module.exports = function(_write, logLevel) {
|
|||||||
logger[levelKey].fail();
|
logger[levelKey].fail();
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return logger;
|
return logger;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
var _ = require('lodash');
|
var _ = require("lodash");
|
||||||
|
|
||||||
// Cleans up an article/chapter object
|
// Cleans up an article/chapter object
|
||||||
// remove 'articles' attributes
|
// remove "articles" attributes
|
||||||
function clean(obj) {
|
function clean(obj) {
|
||||||
return obj && _.omit(obj, ['articles']);
|
return obj && _.omit(obj, ["articles"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenChapters(chapters) {
|
function flattenChapters(chapters) {
|
||||||
@@ -27,7 +27,7 @@ function navigation(summary, files) {
|
|||||||
files = _.isArray(files) ? files : (_.isString(files) ? [files] : null);
|
files = _.isArray(files) ? files : (_.isString(files) ? [files] : null);
|
||||||
|
|
||||||
// List of all navNodes
|
// List of all navNodes
|
||||||
// Flatten chapters, then add in default README node if needed etc ...
|
// Flatten chapters
|
||||||
var navNodes = flattenChapters(summary.chapters);
|
var navNodes = flattenChapters(summary.chapters);
|
||||||
|
|
||||||
// Mapping of prev/next for a give path
|
// Mapping of prev/next for a give path
|
||||||
@@ -39,8 +39,7 @@ function navigation(summary, files) {
|
|||||||
if(!current.exists) return null;
|
if(!current.exists) return null;
|
||||||
|
|
||||||
// Find prev
|
// Find prev
|
||||||
prev = _.chain(navNodes)
|
prev = _.chain(navNodes.slice(0, i))
|
||||||
.slice(0, i)
|
|
||||||
.reverse()
|
.reverse()
|
||||||
.find(function(node) {
|
.find(function(node) {
|
||||||
return node.exists && !node.external;
|
return node.exists && !node.external;
|
||||||
@@ -48,14 +47,12 @@ function navigation(summary, files) {
|
|||||||
.value();
|
.value();
|
||||||
|
|
||||||
// Find next
|
// Find next
|
||||||
next = _.chain(navNodes)
|
next = _.chain(navNodes.slice(i+1))
|
||||||
.slice(i+1)
|
|
||||||
.find(function(node) {
|
.find(function(node) {
|
||||||
return node.exists && !node.external;
|
return node.exists && !node.external;
|
||||||
})
|
})
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
|
|
||||||
return [current.path, {
|
return [current.path, {
|
||||||
index: i,
|
index: i,
|
||||||
title: current.title,
|
title: current.title,
|
||||||
|
|||||||
+106
-84
@@ -5,26 +5,28 @@ var path = require('path');
|
|||||||
var cheerio = require('cheerio');
|
var cheerio = require('cheerio');
|
||||||
var domSerializer = require('dom-serializer');
|
var domSerializer = require('dom-serializer');
|
||||||
var request = require('request');
|
var request = require('request');
|
||||||
var crc = require("crc");
|
var crc = require('crc');
|
||||||
|
var slug = require('github-slugid');
|
||||||
|
|
||||||
var links = require('./links');
|
var links = require('./links');
|
||||||
var imgUtils = require('./images');
|
var imgUtils = require('./images');
|
||||||
var fs = require('./fs');
|
var fs = require('./fs');
|
||||||
var batch = require('./batch');
|
var batch = require('./batch');
|
||||||
var code = require('./code');
|
|
||||||
|
var parsableExtensions = require('gitbook-parsers').extensions;
|
||||||
|
|
||||||
|
// Map of images that have been converted
|
||||||
|
var imgConversionCache = {};
|
||||||
|
|
||||||
// Render a cheerio dom as html
|
// Render a cheerio dom as html
|
||||||
var renderDom = function($, dom, options) {
|
function renderDom($, dom, options) {
|
||||||
if (!dom && $._root && $._root.children) {
|
if (!dom && $._root && $._root.children) {
|
||||||
dom = $._root.children;
|
dom = $._root.children;
|
||||||
}
|
}
|
||||||
|
|
||||||
options = options|| dom.options || $._options;
|
options = options|| dom.options || $._options;
|
||||||
return domSerializer(dom, options);
|
return domSerializer(dom, options);
|
||||||
};
|
}
|
||||||
|
|
||||||
// Map of images that have been converted
|
|
||||||
var imgConversionCache = {};
|
|
||||||
|
|
||||||
function replaceText($, el, search, replace, text_only ) {
|
function replaceText($, el, search, replace, text_only ) {
|
||||||
return $(el).each(function(){
|
return $(el).each(function(){
|
||||||
@@ -39,7 +41,8 @@ function replaceText($, el, search, replace, text_only ) {
|
|||||||
if ( node ) {
|
if ( node ) {
|
||||||
|
|
||||||
// Loop over all childNodes.
|
// Loop over all childNodes.
|
||||||
do {
|
while (node) {
|
||||||
|
|
||||||
// Only process text nodes.
|
// Only process text nodes.
|
||||||
if ( node.nodeType === 3 ) {
|
if ( node.nodeType === 3 ) {
|
||||||
|
|
||||||
@@ -67,17 +70,18 @@ function replaceText($, el, search, replace, text_only ) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} while ( node = node.nextSibling );
|
node = node.nextSibling;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Time to remove those elements!
|
// Time to remove those elements!
|
||||||
remove.length && $(remove).remove();
|
if (remove.length) $(remove).remove();
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
function pregQuote( str ) {
|
function pregQuote( str ) {
|
||||||
return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
|
return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, '\\$1');
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
// Adapt an html snippet to be relative to a base folder
|
// Adapt an html snippet to be relative to a base folder
|
||||||
@@ -98,23 +102,31 @@ function normalizeHtml(src, options) {
|
|||||||
|
|
||||||
// Find svg images to extract and process
|
// Find svg images to extract and process
|
||||||
if (options.convertImages) {
|
if (options.convertImages) {
|
||||||
$("svg").each(function() {
|
$('svg').each(function() {
|
||||||
var content = renderDom($, $(this));
|
var content = renderDom($, $(this));
|
||||||
var svgId = _.uniqueId("svg");
|
var svgId = _.uniqueId('svg');
|
||||||
var dest = svgId+".svg";
|
var dest = svgId+'.svg';
|
||||||
|
|
||||||
// Generate filename
|
// Generate filename
|
||||||
dest = "/"+fs.getUniqueFilename(outputRoot, dest);
|
dest = '/'+fs.getUniqueFilename(outputRoot, dest);
|
||||||
|
|
||||||
svgContent[dest] = '<?xml version="1.0" encoding="UTF-8"?>'+content;
|
svgContent[dest] = '<?xml version="1.0" encoding="UTF-8"?>'+content;
|
||||||
$(this).replaceWith($("<img>").attr("src", dest));
|
$(this).replaceWith($('<img>').attr('src', dest));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate ID for headings
|
||||||
|
$('h1,h2,h3,h4,h5,h6').each(function() {
|
||||||
|
if ($(this).attr('id')) return;
|
||||||
|
|
||||||
|
$(this).attr('id', slug($(this).text()));
|
||||||
|
});
|
||||||
|
|
||||||
// Find images to normalize
|
// Find images to normalize
|
||||||
$("img").each(function() {
|
$('img').each(function() {
|
||||||
var origin = undefined;
|
var origin;
|
||||||
var src = $(this).attr("src");
|
var src = $(this).attr('src');
|
||||||
|
|
||||||
if (!src) return;
|
if (!src) return;
|
||||||
var isExternal = links.isExternal(src);
|
var isExternal = links.isExternal(src);
|
||||||
|
|
||||||
@@ -128,13 +140,13 @@ function normalizeHtml(src, options) {
|
|||||||
// If image is external and ebook, then downlaod the images
|
// If image is external and ebook, then downlaod the images
|
||||||
if (isExternal) {
|
if (isExternal) {
|
||||||
origin = src;
|
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);
|
src = links.toAbsolute(src, options.base, options.output);
|
||||||
isExternal = false;
|
isExternal = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ext = path.extname(src);
|
var ext = path.extname(src);
|
||||||
var srcAbs = links.join("/", options.base, src);
|
var srcAbs = links.join('/', options.base, src);
|
||||||
|
|
||||||
// Test image extension
|
// Test image extension
|
||||||
if (_.contains(imgUtils.INVALID, ext)) {
|
if (_.contains(imgUtils.INVALID, ext)) {
|
||||||
@@ -143,29 +155,29 @@ function normalizeHtml(src, options) {
|
|||||||
src = imgConversionCache[outputRoot][srcAbs];
|
src = imgConversionCache[outputRoot][srcAbs];
|
||||||
} else {
|
} else {
|
||||||
// Not converted yet
|
// Not converted yet
|
||||||
var dest = "";
|
var dest = '';
|
||||||
|
|
||||||
// Replace extension
|
// Replace extension
|
||||||
dest = links.join(path.dirname(srcAbs), path.basename(srcAbs, ext)+".png");
|
dest = links.join(path.dirname(srcAbs), path.basename(srcAbs, ext)+'.png');
|
||||||
dest = dest[0] == "/"? dest.slice(1) : dest;
|
dest = dest[0] == '/'? dest.slice(1) : dest;
|
||||||
|
|
||||||
// Get a name that doesn't exists
|
// Get a name that doesn't exists
|
||||||
dest = fs.getUniqueFilename(outputRoot, dest);
|
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
|
// Add to cache
|
||||||
imgConversionCache[outputRoot][srcAbs] = "/"+dest;
|
imgConversionCache[outputRoot][srcAbs] = '/'+dest;
|
||||||
|
|
||||||
// Push to convert
|
// Push to convert
|
||||||
toConvert.push({
|
toConvert.push({
|
||||||
origin: origin,
|
origin: origin,
|
||||||
content: svgContent[srcAbs],
|
content: svgContent[srcAbs],
|
||||||
source: isExternal? srcAbs : path.join("./", srcAbs),
|
source: isExternal? srcAbs : path.join('./', srcAbs),
|
||||||
dest: path.join("./", dest)
|
dest: path.join('./', dest)
|
||||||
});
|
});
|
||||||
|
|
||||||
src = links.join("/", dest);
|
src = links.join('/', dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset as relative to output
|
// Reset as relative to output
|
||||||
@@ -176,17 +188,17 @@ function normalizeHtml(src, options) {
|
|||||||
// Need to downlaod image
|
// Need to downlaod image
|
||||||
toConvert.push({
|
toConvert.push({
|
||||||
origin: origin,
|
origin: origin,
|
||||||
source: path.join("./", srcAbs)
|
source: path.join('./', srcAbs)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(this).attr("src", src);
|
$(this).attr('src', src);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Normalize links
|
// Normalize links
|
||||||
$("a").each(function() {
|
$('a').each(function() {
|
||||||
var href = $(this).attr("href");
|
var href = $(this).attr('href');
|
||||||
if (!href) return;
|
if (!href) return;
|
||||||
|
|
||||||
if (links.isAnchor(href)) {
|
if (links.isAnchor(href)) {
|
||||||
@@ -194,56 +206,75 @@ function normalizeHtml(src, options) {
|
|||||||
} else if (links.isRelative(href)) {
|
} else if (links.isRelative(href)) {
|
||||||
var parts = url.parse(href);
|
var parts = url.parse(href);
|
||||||
var absolutePath = links.join(options.base, parts.pathname);
|
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 is in navigation relative: transform as content
|
||||||
if (options.navigation[absolutePath]) {
|
if (options.navigation[absolutePath]) {
|
||||||
absolutePath = options.book.contentLink(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
|
// Transform as absolute
|
||||||
href = links.toAbsolute("/"+absolutePath, options.base, options.output)+anchor;
|
href = links.toAbsolute('/'+absolutePath, options.base, options.output)+anchor;
|
||||||
} else {
|
} else {
|
||||||
// External links
|
// External links
|
||||||
$(this).attr("target", "_blank");
|
$(this).attr('target', '_blank');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform extension
|
// Transform extension
|
||||||
$(this).attr("href", href);
|
$(this).attr('href', href);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Highlight code blocks
|
// Highlight code blocks
|
||||||
$("code").each(function() {
|
$('code').each(function() {
|
||||||
// Extract language
|
// Normalize language
|
||||||
var lang = _.chain(
|
var lang = _.chain(
|
||||||
($(this).attr("class") || "").split(" ")
|
($(this).attr('class') || '').split(' ')
|
||||||
)
|
)
|
||||||
.map(function(cl) {
|
.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;
|
return null;
|
||||||
})
|
})
|
||||||
.compact()
|
.compact()
|
||||||
.first()
|
.first()
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
if (lang) {
|
var source = $(this).text();
|
||||||
var html = code.highlight(
|
var blk = options.book.template.applyBlock('code', {
|
||||||
lang,
|
body: source,
|
||||||
$(this).text()
|
kwargs: {
|
||||||
);
|
language: lang
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$(this).html(html);
|
if (blk.html === false) $(this).text(blk.body);
|
||||||
}
|
else $(this).html(blk.body);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Replace glossary terms
|
// Replace glossary terms
|
||||||
_.each(options.glossary, function(term) {
|
var glossary = _.sortBy(options.glossary, function(term) {
|
||||||
var r = new RegExp( "\\b(" + pregQuote(term.name.toLowerCase()) + ")\\b" , 'gi' );
|
return -term.name.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
_.each(glossary, function(term) {
|
||||||
|
var r = new RegExp( '\\b(' + pregQuote(term.name.toLowerCase()) + ')\\b' , 'gi' );
|
||||||
var includedInFiles = false;
|
var includedInFiles = false;
|
||||||
|
|
||||||
$("*").each(function() {
|
$('*').each(function() {
|
||||||
// Ignore codeblocks
|
// Ignore codeblocks
|
||||||
if (_.contains(["code", "pre"], this.name.toLowerCase())) return;
|
if (_.contains(['code', 'pre', 'a'], this.name.toLowerCase())) return;
|
||||||
|
|
||||||
replaceText($, this, r, function(match) {
|
replaceText($, this, r, function(match) {
|
||||||
// Add to files index in glossary
|
// Add to files index in glossary
|
||||||
@@ -252,7 +283,7 @@ function normalizeHtml(src, options) {
|
|||||||
term.files = term.files || [];
|
term.files = term.files || [];
|
||||||
term.files.push(options.navigation[options.input]);
|
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>';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -261,14 +292,14 @@ function normalizeHtml(src, options) {
|
|||||||
html: renderDom($),
|
html: renderDom($),
|
||||||
images: toConvert
|
images: toConvert
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
// Convert svg images to png
|
// Convert svg images to png
|
||||||
function convertImages(images, options) {
|
function convertImages(images, options) {
|
||||||
if (!options.convertImages) return Q();
|
if (!options.convertImages) return Q();
|
||||||
|
|
||||||
var downloaded = [];
|
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, {
|
return batch.execEach(images, {
|
||||||
max: 100,
|
max: 100,
|
||||||
@@ -280,9 +311,15 @@ function convertImages(images, options) {
|
|||||||
// Write image if need to be download
|
// Write image if need to be download
|
||||||
.then(function() {
|
.then(function() {
|
||||||
if (!image.origin && !_.contains(downloaded, image.origin)) return;
|
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);
|
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
|
// Write svg if content
|
||||||
@@ -295,16 +332,15 @@ function convertImages(images, options) {
|
|||||||
.then(function() {
|
.then(function() {
|
||||||
if (!image.dest) return;
|
if (!image.dest) return;
|
||||||
var imgout = path.resolve(options.book.options.output, image.dest);
|
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));
|
return options.book.log.debug.promise(imgUtils.convertSVG(imgin, imgout));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(function() {
|
.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
|
// Adapt page content to be relative to a base folder
|
||||||
function normalizePage(sections, options) {
|
function normalizePage(sections, options) {
|
||||||
@@ -316,16 +352,16 @@ function normalizePage(sections, options) {
|
|||||||
convertImages: false,
|
convertImages: false,
|
||||||
|
|
||||||
// Current file path
|
// Current file path
|
||||||
input: ".",
|
input: '.',
|
||||||
|
|
||||||
// Navigation to use to transform path
|
// Navigation to use to transform path
|
||||||
navigation: {},
|
navigation: {},
|
||||||
|
|
||||||
// Directory parent of the file currently in rendering process
|
// Directory parent of the file currently in rendering process
|
||||||
base: "./",
|
base: './',
|
||||||
|
|
||||||
// Directory parent from the html output
|
// Directory parent from the html output
|
||||||
output: "./",
|
output: './',
|
||||||
|
|
||||||
// Glossary terms
|
// Glossary terms
|
||||||
glossary: []
|
glossary: []
|
||||||
@@ -335,9 +371,9 @@ function normalizePage(sections, options) {
|
|||||||
var toConvert = [];
|
var toConvert = [];
|
||||||
|
|
||||||
sections = _.map(sections, function(section) {
|
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);
|
toConvert = toConvert.concat(out.images);
|
||||||
section.content = out.html;
|
section.content = out.html;
|
||||||
@@ -350,23 +386,9 @@ function normalizePage(sections, options) {
|
|||||||
return convertImages(toConvert, options);
|
return convertImages(toConvert, options);
|
||||||
})
|
})
|
||||||
.thenResolve(sections);
|
.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 = {
|
module.exports = {
|
||||||
normalize: normalizePage,
|
normalize: normalizePage
|
||||||
extractText: extractText
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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");
|
var _ = require("lodash");
|
||||||
|
|
||||||
// Returns from a navigation and a current file, a snapshot of current detailed state
|
// 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 n = _.size(navigation);
|
||||||
var percent = 0, prevPercent = 0, currentChapter = null;
|
var percent = 0, prevPercent = 0, currentChapter = null;
|
||||||
var done = true;
|
var done = true;
|
||||||
|
|||||||
+15
-17
@@ -1,11 +1,9 @@
|
|||||||
var Q = require('q');
|
var Q = require("q");
|
||||||
var _ = require('lodash');
|
var events = require("events");
|
||||||
|
var http = require("http");
|
||||||
var events = require('events');
|
var send = require("send");
|
||||||
var http = require('http');
|
var util = require("util");
|
||||||
var send = require('send');
|
var url = require("url");
|
||||||
var util = require('util');
|
|
||||||
var url = require('url');
|
|
||||||
|
|
||||||
var Server = function() {
|
var Server = function() {
|
||||||
this.running = null;
|
this.running = null;
|
||||||
@@ -17,7 +15,7 @@ util.inherits(Server, events.EventEmitter);
|
|||||||
|
|
||||||
// Return true if the server is running
|
// Return true if the server is running
|
||||||
Server.prototype.isRunning = function() {
|
Server.prototype.isRunning = function() {
|
||||||
return this.running != null;
|
return !!this.running;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Stop the server
|
// Stop the server
|
||||||
@@ -57,25 +55,25 @@ Server.prototype.start = function(dir, port) {
|
|||||||
res.end(err.message);
|
res.end(err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to directory's index.html
|
// Redirect to directory"s index.html
|
||||||
function redirect() {
|
function redirect() {
|
||||||
res.statusCode = 301;
|
res.statusCode = 301;
|
||||||
res.setHeader('Location', req.url + '/');
|
res.setHeader("Location", req.url + "/");
|
||||||
res.end('Redirecting to ' + req.url + '/');
|
res.end("Redirecting to " + req.url + "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send file
|
// Send file
|
||||||
send(req, url.parse(req.url).pathname)
|
send(req, url.parse(req.url).pathname)
|
||||||
.root(dir)
|
.root(dir)
|
||||||
.on('error', error)
|
.on("error", error)
|
||||||
.on('directory', redirect)
|
.on("directory", redirect)
|
||||||
.pipe(res);
|
.pipe(res);
|
||||||
});
|
});
|
||||||
|
|
||||||
that.running.on('connection', function (socket) {
|
that.running.on("connection", function (socket) {
|
||||||
that.sockets.push(socket);
|
that.sockets.push(socket);
|
||||||
socket.setTimeout(4000);
|
socket.setTimeout(4000);
|
||||||
socket.on('close', function () {
|
socket.on("close", function () {
|
||||||
that.sockets.splice(that.sockets.indexOf(socket), 1);
|
that.sockets.splice(that.sockets.indexOf(socket), 1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -91,6 +89,6 @@ Server.prototype.start = function(dir, port) {
|
|||||||
|
|
||||||
return d.promise;
|
return d.promise;
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
module.exports = Server;
|
module.exports = Server;
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
var _ = require("lodash");
|
var _ = require("lodash");
|
||||||
|
|
||||||
function escapeShellArg(arg) {
|
function escapeShellArg(arg) {
|
||||||
var ret = '';
|
var ret = "";
|
||||||
|
|
||||||
ret = arg.replace(/"/g, '\\"');
|
ret = arg.replace(/"/g, '\\"');
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ function escapeShellArg(arg) {
|
|||||||
function optionsToShellArgs(options) {
|
function optionsToShellArgs(options) {
|
||||||
return _.chain(options)
|
return _.chain(options)
|
||||||
.map(function(value, key) {
|
.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;
|
if (value === true) return key;
|
||||||
return key+"="+escapeShellArg(value);
|
return key+"="+escapeShellArg(value);
|
||||||
})
|
})
|
||||||
|
|||||||
+6
-5
@@ -1,9 +1,9 @@
|
|||||||
var Q = require('q');
|
var Q = require("q");
|
||||||
var _ = require('lodash');
|
var _ = require("lodash");
|
||||||
var path = require('path');
|
var path = require("path");
|
||||||
var chokidar = require('chokidar');
|
var chokidar = require("chokidar");
|
||||||
|
|
||||||
var parsers = require('gitbook-parsers')
|
var parsers = require("gitbook-parsers");
|
||||||
|
|
||||||
function watch(dir) {
|
function watch(dir) {
|
||||||
var d = Q.defer();
|
var d = Q.defer();
|
||||||
@@ -19,6 +19,7 @@ function watch(dir) {
|
|||||||
|
|
||||||
var watcher = chokidar.watch(toWatch, {
|
var watcher = chokidar.watch(toWatch, {
|
||||||
cwd: dir,
|
cwd: dir,
|
||||||
|
ignored: "_book/**",
|
||||||
ignoreInitial: true
|
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
|
||||||
|
};
|
||||||
+34
-19
@@ -1,27 +1,30 @@
|
|||||||
{
|
{
|
||||||
"name": "gitbook",
|
"name": "gitbook",
|
||||||
"version": "2.0.3",
|
"version": "2.5.2",
|
||||||
"homepage": "https://www.gitbook.com",
|
"homepage": "https://www.gitbook.com",
|
||||||
"description": "Library and cmd utility to generate GitBooks",
|
"description": "Library and cmd utility to generate GitBooks",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"q": "1.0.1",
|
"q": "1.0.1",
|
||||||
"lunr": "0.5.7",
|
"lodash": "3.10.1",
|
||||||
"lodash": "3.5.0",
|
|
||||||
"graceful-fs": "3.0.5",
|
"graceful-fs": "3.0.5",
|
||||||
"resolve": "0.6.3",
|
"resolve": "0.6.3",
|
||||||
"fs-extra": "0.16.5",
|
"fs-extra": "0.16.5",
|
||||||
"fstream-ignore": "1.0.2",
|
"fstream-ignore": "1.0.2",
|
||||||
"gitbook-parsers": "0.7.5",
|
"gitbook-parsers": "0.8.7",
|
||||||
"nunjucks": "1.3.0",
|
"gitbook-plugin-highlight": "1.0.3",
|
||||||
"nunjucks-autoescape": "0.1.1",
|
"gitbook-plugin-sharing": "1.0.1",
|
||||||
"nunjucks-filter": "0.1.0",
|
"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",
|
"i18n": "0.5.0",
|
||||||
"semver": "2.2.1",
|
"semver": "5.0.1",
|
||||||
"npmi": "0.1.1",
|
"npmi": "0.1.1",
|
||||||
"cheerio": "0.19.0",
|
"cheerio": "0.19.0",
|
||||||
"gitbook-plugin-livereload": "0.0.1",
|
"gitbook-plugin-livereload": "0.0.1",
|
||||||
"chokidar": "~1.0.1",
|
"chokidar": "~1.0.5",
|
||||||
"send": "0.2.0",
|
"send": "0.2.0",
|
||||||
"tiny-lr": "0.1.5",
|
"tiny-lr": "0.1.5",
|
||||||
"tmp": "0.0.24",
|
"tmp": "0.0.24",
|
||||||
@@ -32,18 +35,30 @@
|
|||||||
"npm": "2.4.1",
|
"npm": "2.4.1",
|
||||||
"dom-serializer": "0.1.0",
|
"dom-serializer": "0.1.0",
|
||||||
"spawn-cmd": "0.0.2",
|
"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",
|
||||||
|
"github-slugid": "1.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mocha": "2.2.1",
|
"eslint": "1.5.0",
|
||||||
"should": "5.2.0",
|
"mocha": "2.3.2",
|
||||||
"grunt": "~0.4.2",
|
"should": "7.1.0",
|
||||||
"grunt-cli": "0.1.11",
|
"should-promised": "0.3.1",
|
||||||
"grunt-contrib-copy": "0.5.0",
|
"gulp": "^3.8.11",
|
||||||
"grunt-contrib-less": "~0.5.0",
|
"gulp-rename": "^1.2.2",
|
||||||
"grunt-contrib-requirejs": "0.4.1",
|
"gulp-uglify": "1.1.0",
|
||||||
"grunt-bower-install-simple": "0.9.2"
|
"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": {
|
"scripts": {
|
||||||
"test": "node_modules/.bin/mocha --reporter spec --timeout 15000"
|
"test": "node_modules/.bin/mocha --reporter spec --timeout 15000"
|
||||||
|
|||||||
+23
-8
@@ -4,38 +4,53 @@ var path = require('path');
|
|||||||
var should = require('should');
|
var should = require('should');
|
||||||
var cheerio = require('cheerio');
|
var cheerio = require('cheerio');
|
||||||
|
|
||||||
|
require('should-promised');
|
||||||
|
|
||||||
should.Assertion.add('file', function(file, description) {
|
should.Assertion.add('file', function(file, description) {
|
||||||
this.params = { actual: this.obj.toString(), operator: 'have file ' + file, message: 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.should.have.property('options').which.is.an.Object();
|
||||||
this.obj.options.should.have.property('output').which.is.a.String;
|
this.obj.options.should.have.property('output').which.is.a.String();
|
||||||
this.assert(fs.existsSync(path.resolve(this.obj.options.output, file)));
|
this.assert(fs.existsSync(path.resolve(this.obj.options.output, file)));
|
||||||
});
|
});
|
||||||
|
|
||||||
should.Assertion.add('jsonfile', function(file, description) {
|
should.Assertion.add('jsonfile', function(file, description) {
|
||||||
this.params = { actual: this.obj.toString(), operator: 'have valid jsonfile ' + file, message: 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.should.have.property('options').which.is.an.Object();
|
||||||
this.obj.options.should.have.property('output').which.is.a.String;
|
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.assert(JSON.parse(fs.readFileSync(path.resolve(this.obj.options.output, file), { encoding: 'utf-8' })));
|
||||||
});
|
});
|
||||||
|
|
||||||
should.Assertion.add('html', function(rules, description) {
|
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);
|
var $ = cheerio.load(this.obj);
|
||||||
|
|
||||||
_.each(rules, function(validations, query) {
|
_.each(rules, function(validations, query) {
|
||||||
validations = _.defaults(validations || {}, {
|
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,
|
count: 1,
|
||||||
|
|
||||||
|
// Check attribute values
|
||||||
attributes: {},
|
attributes: {},
|
||||||
|
|
||||||
|
// Trim inner text
|
||||||
trim: false,
|
trim: false,
|
||||||
|
|
||||||
|
// Check inner text
|
||||||
text: undefined
|
text: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
var $el = $(query);
|
var $el = $(query);
|
||||||
|
|
||||||
|
// Select correct element
|
||||||
|
if (_.isNumber(validations.index)) $el = $($el.get(validations.index));
|
||||||
|
|
||||||
// Test number of elements
|
// Test number of elements
|
||||||
$el.should.have.lengthOf(validations.count);
|
$el.length.should.be.equal(validations.count);
|
||||||
|
|
||||||
// Test text
|
// Test text
|
||||||
if (validations.text !== undefined) {
|
if (validations.text !== undefined) {
|
||||||
@@ -47,7 +62,7 @@ should.Assertion.add('html', function(rules, description) {
|
|||||||
// Test attributes
|
// Test attributes
|
||||||
_.each(validations.attributes, function(value, name) {
|
_.each(validations.attributes, function(value, name) {
|
||||||
var attr = $el.attr(name);
|
var attr = $el.attr(name);
|
||||||
should(attr).be.ok;
|
should(attr).be.ok();
|
||||||
attr.should.be.equal(value);
|
attr.should.be.equal(value);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
# Readme
|
# Readme
|
||||||
|
|
||||||
|
<p id="test-plugin-block-shortcuts-1">$$test_block1$$</p>
|
||||||
|
<p id="test-plugin-block-shortcuts-2">{% include "./block.md" %}</p>
|
||||||
|
|
||||||
### Relative
|
### Relative
|
||||||
|
|
||||||
<p id="t1">{% include "./hello.md" %}</p>
|
<p id="t1">{% include "./hello.md" %}</p>
|
||||||
|
<p id="t2">{% include "/hello.md" %}</p>
|
||||||
|
|
||||||
### Git
|
### 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/test.md" %}</p>
|
||||||
<p id="t3">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test2.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$$
|
||||||
@@ -7,3 +7,7 @@ Just a simple and easy to understand test.
|
|||||||
## hakunamatata
|
## hakunamatata
|
||||||
|
|
||||||
This word is not present in the content.
|
This word is not present in the content.
|
||||||
|
|
||||||
|
## test long
|
||||||
|
|
||||||
|
This is a test with a longer text that the first entry to test order.
|
||||||
|
|||||||
@@ -6,3 +6,5 @@ The word test should be replaced by a glossary link.
|
|||||||
But the word test should not be replaced in code blocks
|
But the word test should not be replaced in code blocks
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The two words test long should be replaced by the correct glossary links.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Hello World
|
||||||
|
|
||||||
|
## Hello {#hello-custom}
|
||||||
@@ -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 2](hello2.md)
|
||||||
* Hello 3
|
* Hello 3
|
||||||
* [Hello 4](hello3/hello4.md)
|
* [Hello 4](hello3/hello4.md)
|
||||||
|
* Hello 5
|
||||||
|
* [Hello 6](hello3/hello5/hello6.md)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Readme
|
||||||
|
|
||||||
|
Default description for the book.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# Summary
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
body {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
@@ -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 () {
|
describe('Configuration', function () {
|
||||||
it('should extract default title from README', function() {
|
it('should extract default title from README', function() {
|
||||||
return books.parse("basic")
|
return books.parse('basic')
|
||||||
.then(function(book) {
|
.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() {
|
it('should extract default description from README', function() {
|
||||||
return books.parse("basic")
|
return books.parse('basic')
|
||||||
.then(function(book) {
|
.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() {
|
it('should correctly load from json (book.json)', function() {
|
||||||
return books.parse("config-json")
|
return books.parse('config-json')
|
||||||
.then(function(book) {
|
.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() {
|
it('should correctly load from JavaScript (book.js)', function() {
|
||||||
return books.parse("config-js")
|
return books.parse('config-js')
|
||||||
.then(function(book) {
|
.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;
|
var book, readme;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
return books.generate("conrefs", "website")
|
return books.generate('conrefs', 'website')
|
||||||
.then(function(_book) {
|
.then(function(_book) {
|
||||||
book = _book;
|
book = _book;
|
||||||
|
|
||||||
readme = fs.readFileSync(
|
readme = fs.readFileSync(
|
||||||
path.join(book.options.output, "index.html"),
|
path.join(book.options.output, 'index.html'),
|
||||||
{ encoding: "utf-8" }
|
{ encoding: 'utf-8' }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle local references', function() {
|
it('should handle local references', function() {
|
||||||
readme.should.be.html({
|
readme.should.be.html({
|
||||||
".page-inner p#t1": {
|
'.page-inner p#t1': {
|
||||||
count: 1,
|
count: 1,
|
||||||
text: "Hello World",
|
text: 'Hello World',
|
||||||
trim: true
|
trim: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle git references', function() {
|
it('should handle local references with absolute paths', function() {
|
||||||
readme.should.be.html({
|
readme.should.be.html({
|
||||||
".page-inner p#t2": {
|
'.page-inner p#t2': {
|
||||||
count: 1,
|
count: 1,
|
||||||
text: "Hello from git",
|
text: 'Hello World',
|
||||||
trim: true
|
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,
|
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
|
trim: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+50
-4
@@ -6,19 +6,65 @@ describe('eBook generator', function () {
|
|||||||
var book;
|
var book;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
return books.generate("basic", "ebook")
|
return books.generate('basic', 'ebook')
|
||||||
.then(function(_book) {
|
.then(function(_book) {
|
||||||
book = _book;
|
book = _book;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly output a SUMMARY.html', function() {
|
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() {
|
it('should correctly copy assets', function() {
|
||||||
book.should.have.file("gitbook");
|
book.should.have.file('gitbook');
|
||||||
book.should.have.file("gitbook/style.css");
|
book.should.have.file('gitbook/ebook.css');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not copy website assets', function() {
|
||||||
|
book.should.not.have.file('gitbook/style.css');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Custom styles', function() {
|
||||||
|
var book;
|
||||||
|
|
||||||
|
before(function() {
|
||||||
|
return books.generate('style-print', 'ebook')
|
||||||
|
.then(function(_book) {
|
||||||
|
book = _book;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove default print.css', function() {
|
||||||
|
var PAGE = fs.readFileSync(
|
||||||
|
path.join(book.options.output, 'index.html'),
|
||||||
|
{ encoding: 'utf-8' }
|
||||||
|
);
|
||||||
|
|
||||||
|
// There are 2 styles (one from plugin-highlight and the new style)
|
||||||
|
PAGE.should.be.html({
|
||||||
|
'link': {
|
||||||
|
count: 2
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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 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 should = require("should");
|
||||||
var git = require('../lib/utils/git');
|
var git = require("../lib/utils/git");
|
||||||
|
|
||||||
describe('GIT parser and getter', function () {
|
describe("GIT parser and getter", function () {
|
||||||
it('should correctly parse an https url', function() {
|
it("should correctly parse an https url", function() {
|
||||||
var parts = git.parseUrl("git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md");
|
var parts = git.parseUrl("git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md");
|
||||||
|
|
||||||
should.exist(parts);
|
should.exist(parts);
|
||||||
@@ -11,7 +11,7 @@ describe('GIT parser and getter', function () {
|
|||||||
parts.filepath.should.be.equal("test.md");
|
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");
|
var parts = git.parseUrl("git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md#0.1.2");
|
||||||
|
|
||||||
should.exist(parts);
|
should.exist(parts);
|
||||||
@@ -20,7 +20,7 @@ describe('GIT parser and getter', function () {
|
|||||||
parts.filepath.should.be.equal("test.md");
|
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");
|
var parts = git.parseUrl("git+git@github.com:GitbookIO/gitbook.git/directory/README.md#e1594cde2c32e4ff48f6c4eff3d3d461743d74e1");
|
||||||
|
|
||||||
should.exist(parts);
|
should.exist(parts);
|
||||||
|
|||||||
+23
-14
@@ -1,8 +1,8 @@
|
|||||||
var fs = require('fs');
|
var fs = require("fs");
|
||||||
var path = require('path');
|
var path = require("path");
|
||||||
|
|
||||||
describe('Glossary', function () {
|
describe("Glossary", function () {
|
||||||
describe('Parsing', function() {
|
describe("Parsing", function() {
|
||||||
var book;
|
var book;
|
||||||
|
|
||||||
before(function() {
|
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.should.have.property("glossary");
|
||||||
book.glossary.should.have.lengthOf(2);
|
book.glossary.should.have.lengthOf(3);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Generation', function() {
|
describe("Generation", function() {
|
||||||
var book;
|
var book;
|
||||||
|
|
||||||
before(function() {
|
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");
|
book.should.have.file("GLOSSARY.html");
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Page Integration', function() {
|
describe("Page Integration", function() {
|
||||||
var readme, page;
|
var readme, page;
|
||||||
|
|
||||||
before(function() {
|
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({
|
readme.should.be.html({
|
||||||
".page-inner a[href='GLOSSARY.html#test']": {
|
".page-inner a[href=\"GLOSSARY.html#test\"]": {
|
||||||
count: 1,
|
count: 1,
|
||||||
text: "test",
|
text: "test",
|
||||||
attributes: {
|
attributes: {
|
||||||
@@ -58,21 +58,30 @@ 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.should.be.html({
|
||||||
".page-inner a[href='../GLOSSARY.html#test']": {
|
".page-inner a[href=\"../GLOSSARY.html#test\"]": {
|
||||||
count: 1
|
count: 1
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not replace terms in codeblocks', function() {
|
it("should not replace terms in codeblocks", function() {
|
||||||
readme.should.be.html({
|
readme.should.be.html({
|
||||||
".page-inner code a": {
|
".page-inner code a": {
|
||||||
count: 0
|
count: 0
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should correctly select the longest term", function() {
|
||||||
|
readme.should.be.html({
|
||||||
|
".page-inner a[href=\"GLOSSARY.html#test_long\"]": {
|
||||||
|
count: 1,
|
||||||
|
text: "test long"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
var path = require('path');
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
describe('Headings', function () {
|
||||||
|
var book, PAGE;
|
||||||
|
|
||||||
|
before(function() {
|
||||||
|
return books.generate('headings', 'website')
|
||||||
|
.then(function(_book) {
|
||||||
|
book = _book;
|
||||||
|
|
||||||
|
PAGE = fs.readFileSync(
|
||||||
|
path.join(book.options.output, 'index.html'),
|
||||||
|
{ encoding: 'utf-8' }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('IDs', function() {
|
||||||
|
it('should correctly generate an ID', function() {
|
||||||
|
PAGE.should.be.html({
|
||||||
|
'h1#hello-world': {
|
||||||
|
count: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly accept custom ID', function() {
|
||||||
|
PAGE.should.be.html({
|
||||||
|
'h2#hello-custom': {
|
||||||
|
count: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
+28
-18
@@ -1,15 +1,13 @@
|
|||||||
var os = require('os');
|
var os = require('os');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var Q = require('q');
|
var Q = require('q');
|
||||||
var fs = require('fs');
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var should = require('should');
|
|
||||||
|
|
||||||
var fsUtil = require('../lib/utils/fs');
|
var fsUtil = require('../lib/utils/fs');
|
||||||
var Book = require('../').Book;
|
var Book = require('../').Book;
|
||||||
var LOG_LEVELS = require('../').LOG_LEVELS;
|
var LOG_LEVELS = require('../').LOG_LEVELS;
|
||||||
|
|
||||||
require("./assertions");
|
require('./assertions');
|
||||||
|
|
||||||
|
|
||||||
var BOOKS = {};
|
var BOOKS = {};
|
||||||
@@ -17,30 +15,44 @@ var TMPDIR = os.tmpdir();
|
|||||||
|
|
||||||
|
|
||||||
// Generate and return a book
|
// Generate and return a book
|
||||||
function generateBook(bookId, test) {
|
function generateBook(bookId, test, opts) {
|
||||||
return parseBook(bookId, test)
|
opts = _.defaults(opts || {}, {
|
||||||
|
prepare: function() {}
|
||||||
|
});
|
||||||
|
|
||||||
|
return parseBook(bookId, test, opts)
|
||||||
.then(function(book) {
|
.then(function(book) {
|
||||||
return book.generate(test)
|
|
||||||
|
return Q(opts.prepare(book))
|
||||||
|
.then(function() {
|
||||||
|
return book.generate(test);
|
||||||
|
})
|
||||||
.thenResolve(book);
|
.thenResolve(book);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate and return a book
|
// Generate and return a book
|
||||||
function parseBook(bookId, test) {
|
function parseBook(bookId, test, opts) {
|
||||||
test = test || "website";
|
opts = _.defaults(opts || {}, {
|
||||||
BOOKS[bookId] = BOOKS[bookId] || {};
|
testId: ''
|
||||||
if (BOOKS[bookId][test]) return Q(BOOKS[bookId][test]);
|
});
|
||||||
|
|
||||||
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,
|
logLevel: LOG_LEVELS.DISABLED,
|
||||||
config: {
|
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() {
|
.then(function() {
|
||||||
return BOOKS[bookId][test];
|
return BOOKS[bookId][testId];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,16 +65,14 @@ global.books = {
|
|||||||
// Cleanup all tests
|
// Cleanup all tests
|
||||||
after(function() {
|
after(function() {
|
||||||
return _.chain(BOOKS)
|
return _.chain(BOOKS)
|
||||||
.map(function(types, bookId) {
|
.map(function(types) {
|
||||||
return _.values(types);
|
return _.values(types);
|
||||||
})
|
})
|
||||||
.flatten()
|
.flatten()
|
||||||
.reduce(function(prev, book) {
|
.reduce(function(prev, book) {
|
||||||
return prev.then(function() {
|
return prev.then(function() {
|
||||||
return fsUtil.remove(book.options.output);
|
return fsUtil.remove(book.options.output);
|
||||||
})
|
});
|
||||||
}, Q())
|
}, Q())
|
||||||
.value();
|
.value();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+14
-12
@@ -1,9 +1,9 @@
|
|||||||
var fs = require('fs');
|
var fs = require("fs");
|
||||||
var _ = require('lodash');
|
var _ = require("lodash");
|
||||||
var path = require('path');
|
var path = require("path");
|
||||||
var cheerio = require('cheerio');
|
var cheerio = require("cheerio");
|
||||||
|
|
||||||
describe('Images', function () {
|
describe("Images", function () {
|
||||||
var book, readme, $, $img, srcs;
|
var book, readme, $, $img, srcs;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
@@ -17,38 +17,40 @@ describe('Images', function () {
|
|||||||
);
|
);
|
||||||
$ = cheerio.load(readme);
|
$ = cheerio.load(readme);
|
||||||
$img = $("img");
|
$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);
|
_.uniq(srcs).should.have.lengthOf(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should keep image tags', function() {
|
it("should keep image tags", function() {
|
||||||
srcs.should.have.lengthOf(5);
|
srcs.should.have.lengthOf(5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not have .svg files', function() {
|
it("should not have .svg files", function() {
|
||||||
_.each(srcs, function(src) {
|
_.each(srcs, function(src) {
|
||||||
path.extname(src).should.not.equal(".svg");
|
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) {
|
_.each(srcs, function(src) {
|
||||||
book.should.have.file(src);
|
book.should.have.file(src);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle relative paths', function() {
|
it("should handle relative paths", function() {
|
||||||
var PAGE = fs.readFileSync(
|
var PAGE = fs.readFileSync(
|
||||||
path.join(book.options.output, "folder/PAGE.html"),
|
path.join(book.options.output, "folder/PAGE.html"),
|
||||||
{ encoding: "utf-8" }
|
{ encoding: "utf-8" }
|
||||||
);
|
);
|
||||||
|
|
||||||
PAGE.should.be.html({
|
PAGE.should.be.html({
|
||||||
"img[src='../test.png']": {
|
"img[src=\"../test.png\"]": {
|
||||||
count: 1
|
count: 1
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+6
-5
@@ -9,15 +9,16 @@ describe('Init Books', function () {
|
|||||||
var initRoot;
|
var initRoot;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
initRoot = path.resolve(__dirname, "books/init");
|
initRoot = path.resolve(__dirname, 'books/init');
|
||||||
return Book.init(initRoot, {
|
return Book.init(initRoot, {
|
||||||
logLevel: LOG_LEVELS.DISABLED,
|
logLevel: LOG_LEVELS.DISABLED
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create all chapters', function() {
|
it('should create all chapters', function() {
|
||||||
should(fs.existsSync(path.resolve(initRoot, "hello.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, 'hello2.md'))).be.ok();
|
||||||
should(fs.existsSync(path.resolve(initRoot, "hello3/hello4.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 fs = require("fs");
|
||||||
var path = require('path');
|
var path = require("path");
|
||||||
|
|
||||||
describe('JSON generator', function () {
|
describe("JSON generator", function () {
|
||||||
describe('Basic Book', function() {
|
describe("Basic Book", function() {
|
||||||
var book;
|
var book;
|
||||||
|
|
||||||
before(function() {
|
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");
|
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");
|
book.should.have.jsonfile("README.json");
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Page Format', function() {
|
describe("Page Format", function() {
|
||||||
var page;
|
var page;
|
||||||
|
|
||||||
before(function() {
|
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.should.have.property("sections").with.lengthOf(1);
|
||||||
page.sections[0].should.have.property("content").which.is.a.String;
|
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("type", "normal");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should contains valid progress', function() {
|
it("should contains valid progress", function() {
|
||||||
page.should.have.property("progress");
|
page.should.have.property("progress");
|
||||||
page.progress.should.have.property("chapters").with.lengthOf(1);
|
page.progress.should.have.property("chapters").with.lengthOf(1);
|
||||||
page.progress.should.have.property("current");
|
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);
|
page.should.have.property("langs").with.lengthOf(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Multilingual Book', function() {
|
describe("Multilingual Book", function() {
|
||||||
var book;
|
var book;
|
||||||
|
|
||||||
before(function() {
|
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("README.json");
|
||||||
book.should.have.file("en/README.json");
|
book.should.have.file("en/README.json");
|
||||||
book.should.have.file("fr/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("README.json");
|
||||||
book.should.have.jsonfile("en/README.json");
|
book.should.have.jsonfile("en/README.json");
|
||||||
book.should.have.jsonfile("fr/README.json");
|
book.should.have.jsonfile("fr/README.json");
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Page Format', function() {
|
describe("Page Format", function() {
|
||||||
var page;
|
var page;
|
||||||
|
|
||||||
before(function() {
|
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);
|
page.should.have.property("langs").with.lengthOf(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+5
-8
@@ -1,8 +1,5 @@
|
|||||||
var fs = require('fs');
|
describe("Languages", function () {
|
||||||
var path = require('path');
|
describe("Parsing", function() {
|
||||||
|
|
||||||
describe('Languages', function () {
|
|
||||||
describe('Parsing', function() {
|
|
||||||
var book;
|
var book;
|
||||||
|
|
||||||
before(function() {
|
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.should.have.property("books");
|
||||||
book.books.should.have.lengthOf(2);
|
book.books.should.have.lengthOf(2);
|
||||||
|
|
||||||
@@ -21,7 +18,7 @@ describe('Languages', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Generation', function() {
|
describe("Generation", function() {
|
||||||
var book;
|
var book;
|
||||||
|
|
||||||
before(function() {
|
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("index.html");
|
||||||
book.should.have.file("en/index.html");
|
book.should.have.file("en/index.html");
|
||||||
book.should.have.file("fr/index.html");
|
book.should.have.file("fr/index.html");
|
||||||
|
|||||||
+14
-16
@@ -1,13 +1,11 @@
|
|||||||
var should = require('should');
|
var fs = require("fs");
|
||||||
var fs = require('fs');
|
var path = require("path");
|
||||||
var _ = require('lodash');
|
var cheerio = require("cheerio");
|
||||||
var path = require('path');
|
|
||||||
var cheerio = require('cheerio');
|
|
||||||
|
|
||||||
var links = require("../lib/utils/links");
|
var links = require("../lib/utils/links");
|
||||||
|
|
||||||
describe('Links', function () {
|
describe("Links", function () {
|
||||||
it('should correctly test external links', function() {
|
it("should correctly test external links", function() {
|
||||||
links.isExternal("http://google.fr").should.be.exactly(true);
|
links.isExternal("http://google.fr").should.be.exactly(true);
|
||||||
links.isExternal("https://google.fr").should.be.exactly(true);
|
links.isExternal("https://google.fr").should.be.exactly(true);
|
||||||
links.isExternal("test.md").should.be.exactly(false);
|
links.isExternal("test.md").should.be.exactly(false);
|
||||||
@@ -15,32 +13,32 @@ describe('Links', function () {
|
|||||||
links.isExternal("/folder/test.md").should.be.exactly(false);
|
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(" #test").should.be.exactly(true);
|
links.isAnchor(" #test").should.be.exactly(true);
|
||||||
links.isAnchor("https://google.fr#test").should.be.exactly(false);
|
links.isAnchor("https://google.fr#test").should.be.exactly(false);
|
||||||
links.isAnchor("test.md#test").should.be.exactly(false);
|
links.isAnchor("test.md#test").should.be.exactly(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('toAbsolute', function() {
|
describe("toAbsolute", function() {
|
||||||
it('should correctly transform as absolute', function() {
|
it("should correctly transform as absolute", function() {
|
||||||
links.toAbsolute("http://google.fr").should.be.equal("http://google.fr");
|
links.toAbsolute("http://google.fr").should.be.equal("http://google.fr");
|
||||||
links.toAbsolute("test.md", "./", "./").should.be.equal("test.md");
|
links.toAbsolute("test.md", "./", "./").should.be.equal("test.md");
|
||||||
links.toAbsolute("folder/test.md", "./", "./").should.be.equal("folder/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");
|
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", "./", "./").should.be.equal("test.md");
|
||||||
links.toAbsolute("/test.md", "test", "test").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");
|
links.toAbsolute("/sub/test.md", "test", "test").should.be.equal("../sub/test.md");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('page', function() {
|
describe("page", function() {
|
||||||
var book;
|
var book;
|
||||||
|
|
||||||
before(function() {
|
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(
|
var readme = fs.readFileSync(
|
||||||
path.join(book.options.output, "folder1/index.html"),
|
path.join(book.options.output, "folder1/index.html"),
|
||||||
{ encoding: "utf-8" }
|
{ encoding: "utf-8" }
|
||||||
@@ -58,8 +56,8 @@ describe('Links', function () {
|
|||||||
var $ = cheerio.load(readme);
|
var $ = cheerio.load(readme);
|
||||||
var $a = $(".page-inner a");
|
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 _ = require('lodash');
|
||||||
|
var fs = require('fs');
|
||||||
var should = require('should');
|
var should = require('should');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
||||||
var Plugin = require('../lib/plugin');
|
var Plugin = require('../lib/plugin');
|
||||||
|
var parsers = require('gitbook-parsers');
|
||||||
var PLUGINS_ROOT = path.resolve(__dirname, 'plugins');
|
var PLUGINS_ROOT = path.resolve(__dirname, 'plugins');
|
||||||
|
|
||||||
describe('Plugins', function () {
|
describe('Plugins', function () {
|
||||||
var book;
|
var book;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
return books.parse("basic")
|
return books.parse('basic')
|
||||||
.then(function(_book) {
|
.then(function(_book) {
|
||||||
book = _book;
|
book = _book;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Invalid', function() {
|
describe('Invalid', function() {
|
||||||
var plugin;
|
var plugin;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
plugin = new Plugin(book, "invalid");
|
plugin = new Plugin(book, 'invalid');
|
||||||
plugin.load("./invalid", PLUGINS_ROOT);
|
plugin.load('./invalid', PLUGINS_ROOT);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be detected', function() {
|
it('should be detected', function() {
|
||||||
@@ -32,8 +34,8 @@ describe('Plugins', function () {
|
|||||||
var plugin;
|
var plugin;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
plugin = new Plugin(book, "empty");
|
plugin = new Plugin(book, 'empty');
|
||||||
plugin.load("./empty", PLUGINS_ROOT);
|
plugin.load('./empty', PLUGINS_ROOT);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should valid a plugin', function() {
|
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() {
|
describe('Resources', function() {
|
||||||
var plugin;
|
var plugin;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
plugin = new Plugin(book, "resources");
|
plugin = new Plugin(book, 'resources');
|
||||||
plugin.load("./resources", PLUGINS_ROOT);
|
plugin.load('./resources', PLUGINS_ROOT);
|
||||||
|
|
||||||
return book.plugins.load(plugin);
|
return book.plugins.load(plugin);
|
||||||
});
|
});
|
||||||
@@ -66,29 +99,34 @@ describe('Plugins', function () {
|
|||||||
|
|
||||||
describe('Website', function() {
|
describe('Website', function() {
|
||||||
it('should return a valid list of resources', function() {
|
it('should return a valid list of resources', function() {
|
||||||
return plugin.getResources("website")
|
return plugin.getResources('website')
|
||||||
.then(function(resources) {
|
.then(function(resources) {
|
||||||
resources["js"].should.have.lengthOf(1);
|
resources.js.should.have.lengthOf(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should extend books plugins', function() {
|
it('should extend books plugins', function() {
|
||||||
var resources = book.plugins.resources("website");
|
var resources = book.plugins.resources('website');
|
||||||
resources["js"].should.have.lengthOf(1);
|
resources.js.should.have.lengthOf(5);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('eBook', function() {
|
describe('eBook', function() {
|
||||||
it('should return a valid list of resources', function() {
|
it('should return a valid list of resources', function() {
|
||||||
return plugin.getResources("ebook")
|
return plugin.getResources('ebook')
|
||||||
.then(function(resources) {
|
.then(function(resources) {
|
||||||
resources["css"].should.have.lengthOf(1);
|
resources.css.should.have.lengthOf(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should extend books plugins', function() {
|
it('should extend books plugins', function() {
|
||||||
var resources = book.plugins.resources("ebook");
|
var resources = book.plugins.resources('ebook');
|
||||||
resources["css"].should.have.lengthOf(1);
|
|
||||||
|
// 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;
|
var plugin;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
plugin = new Plugin(book, "filters");
|
plugin = new Plugin(book, 'filters');
|
||||||
plugin.load("./filters", PLUGINS_ROOT);
|
plugin.load('./filters', PLUGINS_ROOT);
|
||||||
|
|
||||||
return book.plugins.load(plugin);
|
return book.plugins.load(plugin);
|
||||||
});
|
});
|
||||||
@@ -111,21 +149,21 @@ describe('Plugins', function () {
|
|||||||
var filters = plugin.getFilters();
|
var filters = plugin.getFilters();
|
||||||
|
|
||||||
_.size(filters).should.equal(2);
|
_.size(filters).should.equal(2);
|
||||||
filters.should.have.property("hello");
|
filters.should.have.property('hello');
|
||||||
filters.should.have.property("helloCtx");
|
filters.should.have.property('helloCtx');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly extend template filters', function() {
|
it('should correctly extend template filters', function() {
|
||||||
return book.template.renderString('{{ "World"|hello }}')
|
return book.template.renderString('{{ \'World\'|hello }}')
|
||||||
.then(function(content) {
|
.then(function(content) {
|
||||||
content.should.equal("Hello World");
|
content.should.equal('Hello World');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly set book as context', function() {
|
it('should correctly set book as context', function() {
|
||||||
return book.template.renderString('{{ "root"|helloCtx }}')
|
return book.template.renderString('{{ \'root\'|helloCtx }}')
|
||||||
.then(function(content) {
|
.then(function(content) {
|
||||||
content.should.equal("root:"+book.root);
|
content.should.equal('root:'+book.root);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -134,15 +172,15 @@ describe('Plugins', function () {
|
|||||||
var plugin;
|
var plugin;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
plugin = new Plugin(book, "blocks");
|
plugin = new Plugin(book, 'blocks');
|
||||||
plugin.load("./blocks", PLUGINS_ROOT);
|
plugin.load('./blocks', PLUGINS_ROOT);
|
||||||
|
|
||||||
return book.plugins.load(plugin);
|
return book.plugins.load(plugin);
|
||||||
});
|
});
|
||||||
|
|
||||||
var testTpl = function(str, args, options) {
|
var testTpl = function(str, args, options) {
|
||||||
return book.template.renderString(str, args, options)
|
return book.template.renderString(str, args, options)
|
||||||
.then(book.template.postProcess)
|
.then(book.template.postProcess);
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should valid a plugin', function() {
|
it('should valid a plugin', function() {
|
||||||
@@ -152,37 +190,139 @@ describe('Plugins', function () {
|
|||||||
it('should correctly extend template blocks', function() {
|
it('should correctly extend template blocks', function() {
|
||||||
return testTpl('{% test %}hello{% endtest %}')
|
return testTpl('{% test %}hello{% endtest %}')
|
||||||
.then(function(content) {
|
.then(function(content) {
|
||||||
content.should.equal("testhellotest");
|
content.should.equal('testhellotest');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly accept shortcuts', function() {
|
describe('Shortcuts', function() {
|
||||||
return testTpl('$$hello$$', {}, {
|
it('should correctly accept shortcuts', function() {
|
||||||
type: "markdown"
|
return testTpl('$$hello$$', {}, {
|
||||||
|
type: 'markdown'
|
||||||
})
|
})
|
||||||
.then(function(content) {
|
.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() {
|
it('should correctly extend template blocks with defined end', function() {
|
||||||
return testTpl('{% test2 %}hello{% endtest2end %}')
|
return testTpl('{% test2 %}hello{% endtest2end %}')
|
||||||
.then(function(content) {
|
.then(function(content) {
|
||||||
content.should.equal("test2hellotest2");
|
content.should.equal('test2hellotest2');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly extend template blocks with sub-blocks', function() {
|
it('should correctly extend template blocks with sub-blocks', function() {
|
||||||
return testTpl('{% test3join separator=";" %}hello{% also %}world{% endtest3join %}')
|
return testTpl('{% test3join separator=";" %}hello{% also %}world{% endtest3join %}')
|
||||||
.then(function(content) {
|
.then(function(content) {
|
||||||
content.should.equal("hello;world");
|
content.should.equal('hello;world');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly extend template blocks with different sub-blocks', function() {
|
it('should correctly extend template blocks with different sub-blocks', function() {
|
||||||
return testTpl('{% test4join separator=";" %}hello{% also %}the{% finally %}world{% endtest4join %}')
|
return testTpl('{% test4join separator=";" %}hello{% also %}the{% finally %}world{% endtest4join %}')
|
||||||
.then(function(content) {
|
.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");
|
assert(blk.blocks[1].name, "finally");
|
||||||
return [blk.body, blk.blocks[0].body, blk.blocks[1].body].join(blk.kwargs.separator);
|
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 fs = require("fs");
|
||||||
var path = require('path');
|
var path = require("path");
|
||||||
|
|
||||||
describe('Summary', function () {
|
describe("Summary", function () {
|
||||||
describe('Parsing', function() {
|
describe("Parsing", function() {
|
||||||
var book;
|
var book;
|
||||||
|
|
||||||
before(function() {
|
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.should.have.property("summary");
|
||||||
book.summary.should.have.property("chapters");
|
book.summary.should.have.property("chapters");
|
||||||
book.summary.chapters.should.have.lengthOf(4);
|
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[0].exists.should.have.equal(true);
|
||||||
book.summary.chapters[1].exists.should.have.equal(true);
|
book.summary.chapters[1].exists.should.have.equal(true);
|
||||||
book.summary.chapters[2].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;
|
var book;
|
||||||
|
|
||||||
before(function() {
|
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("index.html");
|
||||||
book.should.have.file("PAGE1.html");
|
book.should.have.file("PAGE1.html");
|
||||||
book.should.have.file("folder/PAGE2.html");
|
book.should.have.file("folder/PAGE2.html");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly output summary', function() {
|
it("should correctly output summary", function() {
|
||||||
var PAGE = fs.readFileSync(
|
var PAGE = fs.readFileSync(
|
||||||
path.join(book.options.output, "index.html"),
|
path.join(book.options.output, "index.html"),
|
||||||
{ encoding: "utf-8" }
|
{ encoding: "utf-8" }
|
||||||
);
|
);
|
||||||
|
|
||||||
PAGE.should.be.html({
|
PAGE.should.be.html({
|
||||||
".book-summary .chapter[data-level='0'] a": {
|
".book-summary .chapter[data-level=\"0\"] a": {
|
||||||
attributes: {
|
attributes: {
|
||||||
href: "./index.html"
|
href: "./index.html"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
".book-summary .chapter[data-level='1'] a": {
|
".book-summary .chapter[data-level=\"1\"] a": {
|
||||||
attributes: {
|
attributes: {
|
||||||
href: "./PAGE1.html"
|
href: "./PAGE1.html"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
".book-summary .chapter[data-level='2'] a": {
|
".book-summary .chapter[data-level=\"2\"] a": {
|
||||||
attributes: {
|
attributes: {
|
||||||
href: "./folder/PAGE2.html"
|
href: "./folder/PAGE2.html"
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-7
@@ -1,6 +1,6 @@
|
|||||||
var pkg = require("../package.json");
|
var pkg = require("../package.json");
|
||||||
|
|
||||||
describe('Templating', function () {
|
describe("Templating", function () {
|
||||||
var book;
|
var book;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
@@ -12,19 +12,19 @@ describe('Templating', function () {
|
|||||||
|
|
||||||
var testTpl = function(str, args, options) {
|
var testTpl = function(str, args, options) {
|
||||||
return book.template.renderString(str, args, options)
|
return book.template.renderString(str, args, options)
|
||||||
.then(book.template.postProcess)
|
.then(book.template.postProcess);
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Context', function() {
|
describe("Context", function() {
|
||||||
it('should correctly have access to generator', function() {
|
it("should correctly have access to generator", function() {
|
||||||
return testTpl('{{ gitbook.generator }}')
|
return testTpl("{{ gitbook.generator }}")
|
||||||
.then(function(content) {
|
.then(function(content) {
|
||||||
content.should.equal("website");
|
content.should.equal("website");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly have access to gitbook version', function() {
|
it("should correctly have access to gitbook version", function() {
|
||||||
return testTpl('{{ gitbook.version }}')
|
return testTpl("{{ gitbook.version }}")
|
||||||
.then(function(content) {
|
.then(function(content) {
|
||||||
content.should.equal(pkg.version.toString());
|
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('Website generator', function () {
|
||||||
describe('Basic Book', function() {
|
describe('Basic Book', function() {
|
||||||
var book;
|
var book;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
return books.generate("basic", "website")
|
return books.generate('basic', 'website')
|
||||||
.then(function(_book) {
|
.then(function(_book) {
|
||||||
book = _book;
|
book = _book;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly output an index.html', function() {
|
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() {
|
it('should correctly copy assets', function() {
|
||||||
book.should.have.file("gitbook");
|
book.should.have.file('gitbook');
|
||||||
book.should.have.file("gitbook/app.js");
|
book.should.have.file('gitbook/app.js');
|
||||||
book.should.have.file("gitbook/style.css");
|
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.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user