Compare commits

...

114 Commits

Author SHA1 Message Date
Samy Pesse 501843d624 Bump version to 2.2.0 2015-07-28 15:28:38 -07:00
Samy Pesse 508ce4283e Fix #819: codes must always be ltr 2015-07-28 15:25:12 -07:00
Samy Pessé 4aa8e66549 Merge pull request #813 from MrMaksimize/pdf-options
Allowing for custom chapter-mark and page-break in PDF.
2015-07-01 18:00:31 -04:00
Maksim Pecherskiy 917a6f70d8 Allowing for custom chapter-mark and page-break in PDF. 2015-06-30 19:48:51 -07:00
Samy Pesse 2f5e53da55 Update highlight.js@8.6.0 2015-06-29 10:02:35 -04:00
Samy Pesse c3cdefc90a Simpler template for summary 2015-06-29 09:58:23 -04:00
Samy Pesse 9c580e4989 Fix #802: make book.json accessible as "config." in templating 2015-06-19 16:28:46 +02:00
Samy Pessé 50c902c4e4 Merge pull request #805 from GitbookIO/fix/804
Fix #804
2015-06-18 11:16:32 +02:00
Samy Pesse 84b6e0bde6 Add test for kwargs 2015-06-18 11:15:44 +02:00
Samy Pesse abc29c3fba Fix #804: correctly extract kwargs from blocks 2015-06-18 11:11:47 +02:00
Samy Pesse 8356c534f5 Add failing tests for block's arguments 2015-06-18 11:09:02 +02:00
Samy Pessé 78ff2687b4 Merge pull request #797 from nghong/vi-locale
Adding Vietnamese locale support
2015-06-13 11:15:00 +02:00
Hong Nguyen 9b6586fb36 Add translator name to AUTHORS.md 2015-06-13 16:13:34 +07:00
Hong Nguyen ea133219d0 Minor fix 2015-06-13 16:08:21 +07:00
Hong Nguyen c9b5d81a3c Add Vietnamese locale support 2015-06-13 15:42:49 +07:00
Samy Pessé d895c63211 Bump version to 2.1.0 2015-06-02 14:52:11 +02:00
Samy Pessé 8c86da67ca Update gitbook-parsers@0.7.6 2015-06-02 14:47:21 +02:00
Samy Pessé a9a230dfc3 Merge pull request #781 from GitbookIO/fix/775
Fix #775: add optional print.css style
2015-06-02 11:00:22 +02:00
Samy Pessé 6d53e52771 Handle empty string for styles configuration 2015-06-02 11:00:15 +02:00
Samy Pessé 52e10b7cfe Remove default print.css when styles/print.css is present 2015-06-02 10:23:31 +02:00
Samy Pessé addac85696 Add test for custom print style 2015-06-02 10:18:44 +02:00
Samy Pessé 13fb949d68 Merge pull request #774 from wilhelmmatilainen/patch-1
Styling should go to <head>, not to <body>
2015-05-30 18:11:15 +02:00
Wilhelm f1b593cfbd Styling should go to <head>, not to <body>
https://developers.google.com/speed/pagespeed/module/filter-css-to-head
2015-05-29 15:42:40 +03:00
Samy Pesse f998e2dec9 Bump version to 2.0.4 2015-05-28 18:07:20 +02:00
Samy Pesse 2c0bd53a91 Fix i18n used in multilingual book 2015-05-28 17:06:09 +02:00
Samy Pesse 6dd7f6162f Add title to languages chooser
Related to #757
2015-05-28 16:14:03 +02:00
Samy Pesse 475b0bb2b3 Adapt to nunjucks 1.3.x 2015-05-28 15:59:14 +02:00
Samy Pesse e6343935e3 Update nunjucks to fix raw tags 2015-05-28 15:50:46 +02:00
Samy Pessé 946883fd4c Merge pull request #772 from GitbookIO/feature/glossary_order
Fix #768: sort glossary, start with longest matchs
2015-05-28 15:21:03 +02:00
Samy Pessé b1b7e3148b Merge pull request #743 from chudaol/master
Added vk sharing button.
2015-05-28 14:54:03 +02:00
Samy Pesse 32203f6438 Sort glossary before applying it on pages 2015-05-28 14:49:53 +02:00
Samy Pesse f2d3c5d2ed Add test for #768 2015-05-28 14:38:21 +02:00
Samy Pessé 7e2f0ed4bc Merge pull request #758 from aschempp/hotfix/x-ua-compatible
Fix the X-UA-Compatible meta tag
2015-05-28 14:18:19 +02:00
Samy Pessé a877acf905 Merge pull request #759 from aschempp/hotfix/stylesheets
Move style sheets to the <head> section
2015-05-28 14:17:39 +02:00
Samy Pesse ac756ddf7e Bump version to 2.0.3 2015-05-27 21:11:58 +02:00
Samy Pesse 36e2236e6b Fix #765: in init handle correctly empty entries
Add tests for it, fix #769
2015-05-27 21:05:36 +02:00
Samy Pesse 72df665520 Update gitbook-parsers@0.7.5 2015-05-27 19:11:50 +02:00
Samy Pessé 07785528a5 Merge pull request #767 from wilhelmmatilainen/patch-1
Don't create panic by telling about false overwrite
2015-05-27 13:17:05 +02:00
Wilhelm bb579dd581 Don't create panic by telling about false overwrite 2015-05-27 14:13:02 +03:00
Samy Pessé a7d3064340 Change license to valid SPDX code 2015-05-26 16:10:11 +02:00
Andreas Schempp 1a60778248 Move style sheets to the <head> section
see https://github.com/GitbookIO/gitbook/issues/756
2015-05-23 09:14:26 +02:00
Andreas Schempp cda4bb5bda Fix the X-UA-Compatible meta tag
see https://github.com/GitbookIO/gitbook/issues/755
2015-05-23 09:06:58 +02:00
Olga Filipova 7543781046 Added vk sharing button. 2015-05-12 22:30:09 +02:00
Samy Pessé 82f3ffad06 Merge pull request #730 from creynders/patch-1
Fix typo in warning in `lib/plugin`
2015-05-04 14:47:56 +02:00
creynders dc4d2a1731 Fix typo in warning in lib/plugin 2015-05-04 13:16:47 +02:00
Samy Pessé 2523925915 Bump version to 2.0.2 2015-05-02 12:39:05 +02:00
Samy Pesse d5bbd8475c Fix #721: use links.join instead of path.join for links in page 2015-04-23 12:58:24 -05:00
Samy Pesse 54d195268a Add test relative links (related to #721) 2015-04-23 12:05:04 -05:00
Samy Pesse a0deb9509f Merge branch 'awrelll-master' 2015-04-20 07:53:16 -05:00
Samy Pesse b0b1433594 Fix authors list merge 2015-04-20 07:52:12 -05:00
Samy Pessé 17a2c0a37c Merge pull request #706 from savikko/patch-1
Added Finnish author
2015-04-14 18:56:31 -05:00
Samy Pessé fd68ea1cc2 Merge pull request #705 from savikko/master
Added Finnish
2015-04-14 18:56:21 -05:00
Tommi Savikko 784874c776 Added Finnish author 2015-04-14 22:39:23 +03:00
Thomas Clayton f45c6d3466 Added Finnish 2015-04-14 21:50:46 +03:00
Samy Pessé 98af70902f Merge pull request #701 from anthonyfok/patch-1
Fix typo in lib/book.js: lanuages → languages
2015-04-10 20:50:35 -05:00
Anthony Fok 41a67b5c4c Fix typo in lib/book.js: lanuages → languages 2015-04-10 07:41:12 -06:00
Samy Pessé 83ee903af4 Merge pull request #700 from paulmillr/master
Switch from gaze to chokidar.
2015-04-10 10:39:13 +02:00
Samy Pessé e591b0e485 Merge pull request #696 from sindresorhus/escaperegex-module
use the `escape-string-regexp` module
2015-04-10 10:37:53 +02:00
Paul Miller bcc9a34742 Switch from gaze to chokidar. 2015-04-10 06:09:04 +03:00
Sindre Sorhus 6b56c7de7f use the escape-string-regexp module
no point in hardcoding this.

https://github.com/sindresorhus/escape-string-regexp
2015-04-08 18:24:26 +07:00
Iancu Aurel 7b42719699 korean and japanese added 2015-04-07 17:34:40 +03:00
awrelll 64b8343f67 korean support added 2015-04-07 17:32:33 +03:00
awrelll fcc5089fa6 japan support added 2015-04-07 17:26:45 +03:00
Samy Pessé 334f782aa8 Merge pull request #693 from awrelll/patch-1
Romanian author added
2015-04-07 16:21:56 +02:00
Samy Pessé 1776f2bf77 Merge pull request #691 from awrelll/master
support for romanian language
2015-04-07 16:21:34 +02:00
Iancu Aurel e259412f79 Romanian author added 2015-04-07 17:21:05 +03:00
awrelll 999a02f110 support for romanian language 2015-04-07 17:12:41 +03:00
Samy Pessé 604d0fba65 Update nunjucks@1.3.0 2015-04-03 22:31:55 +02:00
Samy Pessé 0e8f7b870b Bump version to 2.0.1 2015-04-02 19:41:14 +02:00
Samy Pessé 1eebc9a46e Normalize structure parsing error 2015-04-02 19:21:39 +02:00
Samy Pessé 0a94c80c44 Normalize template errors 2015-04-02 18:59:56 +02:00
Samy Pessé c70bb2fbd8 Update gitbook-parsers@0.7.4 2015-04-02 18:31:49 +02:00
Samy Pessé 12c06b384f Don't fail if config doesn't exists 2015-04-02 09:43:48 +02:00
Samy Pessé 82a58e7ac3 Invalidate configuration cache when reloading 2015-04-02 09:38:57 +02:00
Samy Pessé dcb56ef433 Merge pull request #683 from GitbookIO/fix/i18n
Fix i18n in json format
2015-04-01 22:23:23 +02:00
Samy Pessé 8da91de514 Don't update translations 2015-04-01 22:21:13 +02:00
Samy Pessé 4b20685562 Use i18n node module for template translation 2015-04-01 22:17:12 +02:00
Samy Pessé fff7c5cc3a Add back support for options.originalInput
Related to rtCamp/gitbook-plugin-edit-link#2
2015-03-30 16:50:26 +02:00
Samy Pessé 3065742b73 Separate book.contentLink and book.contentPath 2015-03-28 15:54:34 +01:00
Samy Pessé ffd69ad1f2 Add test for summary paths
Related to #674
2015-03-28 15:28:28 +01:00
Samy Pessé 9004011479 Merge pull request #673 from lszeremeta/master
Polish (pl) added in theme/i18n
2015-03-28 14:54:50 +01:00
Łukasz Szeremeta 1046bb4133 Add @lszeremeta to translators 2015-03-28 14:48:16 +01:00
Łukasz Szeremeta 5578505213 Polish (pl) added in theme/i18n 2015-03-27 22:03:28 +01:00
Samy Pessé a61f56662a Bump version to 2.0.0 2015-03-25 10:43:58 +01:00
Samy Pessé f42e48be03 Fix scroll reset on small resolutions (mobile & tablet) 2015-03-24 23:59:03 +01:00
Samy Pessé f640e101ba Fix scroling position when preparing page 2015-03-24 23:52:41 +01:00
Samy Pessé b7da276ae0 Fix title for introduction (with i18n) 2015-03-24 23:49:12 +01:00
Samy Pessé bf7c0989f5 Update gitbook-parsers@0.7.2 2015-03-24 23:35:17 +01:00
Samy Pessé 65fdbe7f55 Simplify examples of commands 2015-03-24 23:19:00 +01:00
Samy Pessé c9af209a93 Merge pull request #667 from GitbookIO/better-testing
Better Unit Tests
2015-03-24 23:14:17 +01:00
Samy Pessé 48ab44a776 Add tests for conrefs 2015-03-24 18:22:44 +01:00
Samy Pessé b2e684af8f Add test for templating context 2015-03-24 17:34:47 +01:00
Samy Pessé b38a2c11a4 Add tests for plugins 2015-03-24 17:31:15 +01:00
Samy Pessé c31acef47a Add tests for images in ebook 2015-03-24 16:26:19 +01:00
Samy Pessé e9a573e18a Add test for default description from readme 2015-03-24 14:57:29 +01:00
Samy Pessé c07ddc77f7 Add tests for links 2015-03-24 14:52:24 +01:00
Samy Pessé 303fc992d4 Add test for git url parsing 2015-03-24 14:44:07 +01:00
Samy Pessé 4b7797f61b Add basic tests for multilingual books 2015-03-24 14:16:03 +01:00
Samy Pessé f775d41933 Add base tests for ebook generation 2015-03-24 13:52:29 +01:00
Samy Pessé 7722e7bdfa Update gitbook-parsers@0.7.1 2015-03-24 13:33:56 +01:00
Samy Pessé 346c05f0de Add tests for configuration parsing 2015-03-24 13:24:20 +01:00
Samy Pessé 73c69923b3 Add test for summary parsing
Update gitbook-parsers
2015-03-24 13:11:29 +01:00
Samy Pessé afbca4bdf5 Improve tests for glossary 2015-03-24 11:54:03 +01:00
Samy Pessé b61371942b Add test for glossary replacements 2015-03-24 11:27:37 +01:00
Samy Pessé 61c07e8876 Add test for glossary parsing 2015-03-24 10:24:49 +01:00
Samy Pessé d317741144 Add basic test for multilingual book 2015-03-24 09:36:51 +01:00
Samy Pessé 275739a778 Add basic test for website generation 2015-03-24 08:49:39 +01:00
Samy Pessé cb3a43ed8a Add string representation for books 2015-03-24 08:49:22 +01:00
Samy Pessé ec8d1d8fc9 Improve test for json format 2015-03-23 23:48:46 +01:00
Samy Pessé 868a3186dd Add assertion to test that a file exists 2015-03-23 23:32:07 +01:00
Samy Pessé 17b546181c Fix cleanup after tests 2015-03-23 23:23:25 +01:00
Samy Pessé 2d1aa15af8 New cleaner helper for testing 2015-03-23 23:18:15 +01:00
Samy Pessé 63ee94ff89 Ignore codeblocks when replacing glossary terms 2015-03-23 11:00:50 +01:00
Samy Pessé 7d9dc08a38 Highlight codeblocks in gitbook instead than the parsers 2015-03-23 10:31:48 +01:00
134 changed files with 1513 additions and 756 deletions
+12
View File
@@ -30,6 +30,8 @@ Translators
- Knut Melvær (@kmelve)
- Persian (Farsi)
- Mohammad Hossein Mojtahedi (@mhm5000)
- Polish
- Łukasz Szeremeta (@lszeremeta)
- Portuguese
- Ryan O'Mullan (@RyanOM)
- Spanish
@@ -40,3 +42,13 @@ Translators
- Hu Hao (@howiehu)
- French
- Samy Pessé (@SamyPesse)
- Romanian
- Iancu Aurel (@awrelll)
- Finnish
- Tommi Savikko (@savikko)
- Japanese
- Iancu Aurel (@awrelll)
- Korean
- Iancu Aurel (@awrelll)
- Vietnamese
- Hong Nguyen (@nghong)
+44
View File
@@ -2,6 +2,50 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 2.2.0
- Fix direction in code blocks (always LTR)
- Add options `chapterMark` and `pageBreaksBefore` for PDF
- Update code highlighting library
- `book.json` accessible as `config` in templating syntax
- Add Vietnamese translation (`vi`)
## 2.1.0
- Fix error in calcul of `levels` in table of contents, error introduced a few versions ago
- Add optional `styles/print.css` to replace `print.css` used in ebook
## 2.0.4
- Fix `{% raw %}`, got confused with "fake" variable declarations
- Fix title of language chooser
- Fix the X-UA-Compatible meta tag
- Move style sheets to the <head> section
## 2.0.3
- Fix `gitbook init` for SUMMARY with empty entries
- Fix escaping of code blocks in markdown
## 2.0.2
- Fix relative links in windows
- Improve watcher in serve command (switch to chokidar)
- Add Romanian translation (`ro`)
- Add Finish translation (`fi`)
- Add Japanese translation (`jp`)
- Add Korean translation (`kr`)
## 2.0.1
- Improve error logging (display file, line and column)
- Add back support for `options.originalInput`
- Don't process math in markdown parser (delegated to `mathjax` plugin)
- Fix some cases of code blocks escaping
- Fix i18n for introduction title in json format
- Fix reload when book configuration is updated
- Fix backslashes in url when building on windows
## 2.0.0
- Fix page title of introduction
- Ignore codeblocks when replacing glossary terms
- Improve Unit Tests
- Fix scrolling position in website when preparing page
## 2.0.0-beta.5
- Fix progress order in json format
+2 -2
View File
@@ -32,13 +32,13 @@ $ gitbook init
You can serve a repository as a book using:
```
$ gitbook serve ./repository
$ gitbook serve
```
Or simply build the static website using:
```
$ gitbook build ./repository ./outputFolder
$ gitbook build
```
## Features
+96 -22
View File
@@ -2,9 +2,9 @@ var Q = require("q");
var _ = require("lodash");
var path = require("path");
var util = require("util");
var lunr = require('lunr');
var lunr = require("lunr");
var parsers = require("gitbook-parsers");
var color = require('bash-color');
var color = require("bash-color");
var fs = require("./utils/fs");
var parseNavigation = require("./utils/navigation");
@@ -12,6 +12,7 @@ var parseProgress = require("./utils/progress");
var pageUtil = require("./utils/page");
var batch = require("./utils/batch");
var links = require("./utils/links");
var i18n = require("./utils/i18n");
var logger = require("./utils/logger");
var Configuration = require("./configuration");
@@ -94,6 +95,11 @@ var Book = function(root, context, parent) {
_.bindAll(this);
};
// Return string representation
Book.prototype.toString = function() {
return "[Book "+this.root+"]";
};
// Initialize and parse the book: config, summary, glossary
Book.prototype.parse = function() {
var that = this;
@@ -109,7 +115,7 @@ Book.prototype.parse = function() {
return that.parseLangs()
.then(function() {
multilingual = that.langs.length > 0;
if (multilingual) that.log.info.ln("Parsing multilingual book, with", that.langs.length, "lanuages");
if (multilingual) that.log.info.ln("Parsing multilingual book, with", that.langs.length, "languages");
// Sub-books that inherit from the current book configuration
that.books = _.map(that.langs, function(lang) {
@@ -229,8 +235,15 @@ Book.prototype.generate = function(generator) {
return _.reduce(ops["content"] || [], function(prev, file, i) {
return prev.then(function() {
var p = ((i*100)/nFiles).toFixed(0)+"%";
that.log.info.ln("processing", file, p);
return Q(generator.convertFile(file));
that.log.debug.ln("processing", file, p);
return Q(generator.convertFile(file))
.fail(function(err) {
// Transform error message to signal file
throw that.normError(err, {
fileName: file
});
});
});
}, Q());
});
@@ -364,7 +377,13 @@ Book.prototype.parseReadme = function() {
that.log.debug.ln("readme located at", that.readmeFile);
return that.template.renderFile(that.readmeFile)
.then(function(content) {
return readme.parser.readme(content);
return readme.parser.readme(content)
.fail(function(err) {
throw that.normError(err, {
name: err.name || "Readme Parse Error",
fileName: that.readmeFile
});
});
});
})
.then(function(readme) {
@@ -391,7 +410,13 @@ Book.prototype.parseLangs = function() {
that.log.debug.ln("languages index located at", that.langsFile);
return that.template.renderFile(that.langsFile)
.then(function(content) {
return langs.parser.langs(content);
return langs.parser.langs(content)
.fail(function(err) {
throw that.normError(err, {
name: err.name || "Langs Parse Error",
fileName: that.langsFile
});
});
});
})
.then(function(langs) {
@@ -420,8 +445,14 @@ Book.prototype.parseSummary = function() {
.then(function(content) {
return summary.parser.summary(content, {
entryPoint: that.readmeFile,
entryPointTitle: "",
entryPointTitle: that.i18n('SUMMARY_INTRODUCTION'),
files: that.files
})
.fail(function(err) {
throw that.normError(err, {
name: err.name || "Summary Parse Error",
fileName: that.summaryFile
});
});
});
})
@@ -450,7 +481,13 @@ Book.prototype.parseGlossary = function() {
that.log.debug.ln("glossary located at", that.glossaryFile);
return that.template.renderFile(that.glossaryFile)
.then(function(content) {
return glossary.parser.glossary(content);
return glossary.parser.glossary(content)
.fail(function(err) {
throw that.normError(err, {
name: err.name || "Glossary Parse Error",
fileName: that.glossaryFile
});
});
});
})
.then(function(glossary) {
@@ -670,8 +707,8 @@ Book.prototype.resolve = function(p) {
return path.resolve(this.root, p);
};
// Normalize a link to .html and convert README -> index
Book.prototype.contentLink = function(link) {
// Normalize a path to .html and convert README -> index
Book.prototype.contentPath = function(link) {
if (
path.basename(link, path.extname(link)) == "README"
|| link == this.readmeFile
@@ -683,6 +720,11 @@ Book.prototype.contentLink = function(link) {
return link;
}
// Normalize a link to .html and convert README -> index
Book.prototype.contentLink = function(link) {
return links.normalize(this.contentPath(link));
}
// Index a page into the search index
Book.prototype.indexPage = function(page) {
var nav = this.navigation[page.path];
@@ -717,22 +759,49 @@ Book.prototype.setOutput = function(p) {
});
};
// Translate a strign according to the book language
Book.prototype.i18n = function(phrase) {
var args = Array.prototype.slice.call(arguments);
return i18n.__.apply({}, [this.config.normalizeLanguage()].concat(args));
};
// Normalize error
Book.prototype.normError = function(err, opts, defs) {
if (_.isString(err)) err = new Error(err);
// Extend err
_.extend(err, opts || {});
_.defaults(err, defs || {});
err.lineNumber = err.lineNumber || err.lineno;
err.columnNumber = err.columnNumber || err.colno;
err.toString = function() {
var attributes = [];
if (this.fileName) attributes.push("In file '"+this.fileName+"'");
if (this.lineNumber) attributes.push("Line "+this.lineNumber);
if (this.columnNumber) attributes.push("Column "+this.columnNumber);
return (this.name || "Error")+": "+this.message+((attributes.length > 0)? " ("+attributes.join(", ")+")" : "")
};
return err;
};
// Init and return a book
Book.init = function(root) {
var book = new Book(root);
Book.init = function(root, opts) {
var book = new Book(root, opts);
var extensionToUse = ".md";
var chaptersPaths = function(chapters) {
return _.reduce(chapters || [], function(accu, chapter) {
if (!chapter.path) return accu;
var o = {
title: chapter.title
};
if (chapter.path) o.path = chapter.path;
return accu.concat(
_.filter([
{
title: chapter.title,
path: chapter.path
}
].concat(chaptersPaths(chapter.articles)))
[o].concat(chaptersPaths(chapter.articles))
);
}, []);
};
@@ -769,12 +838,17 @@ Book.init = function(root) {
.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) {
book.log.info.ln("create", chapter.path);
if(exists) return;
if(exists) {
book.log.info.ln("found", chapter.path);
return;
} else {
book.log.info.ln("create", chapter.path);
}
return fs.mkdirp(path.dirname(absolutePath))
.then(function() {
+34 -3
View File
@@ -60,7 +60,6 @@ function normalizePluginsNames(plugins) {
return _.pluck(normalizePluginsList(plugins), "name");
};
var Configuration = function(book, options) {
var that = this;
@@ -75,6 +74,20 @@ var Configuration = function(book, options) {
return that.book.root;
}
});
// options.originalInput == book.parent.root
Object.defineProperty(this.options, "originalInput", {
get: function () {
return that.book.parent? that.book.parent.root : undefined;
}
});
// options.originalOutput == book.parent.options.output
Object.defineProperty(this.options, "originalOutput", {
get: function () {
return that.book.parent? that.book.parent.options.output : undefined;
}
});
};
// Read and parse the configuration
@@ -83,8 +96,17 @@ Configuration.prototype.load = function() {
return Q()
.then(function() {
var configPath, config;
try {
var _config = require(path.resolve(that.book.root, that.options.configFile));
configPath = require.resolve(
path.resolve(that.book.root, that.options.configFile)
);
// Invalidate node.js cache for livreloading
delete require.cache[configPath];
_config = require(configPath);
that.options = _.merge(
that.options,
_.omit(_config, 'configFile', 'defaultsPlugins', 'generator', 'extension')
@@ -111,7 +133,7 @@ Configuration.prototype.load = function() {
// Default value for text direction (from language)
if (!that.options.direction) {
var lang = i18n.getByLanguage(that.options.language);
var lang = i18n.getCatalog(that.options.language);
if (lang) that.options.direction = lang.direction;
}
@@ -164,6 +186,7 @@ Configuration.DEFAULT = {
// CSS Styles
"styles": {
"website": "styles/website.css",
"print": "styles/print.css",
"ebook": "styles/ebook.css",
"pdf": "styles/pdf.css",
"mobi": "styles/mobi.css",
@@ -223,6 +246,14 @@ Configuration.DEFAULT = {
// Choices are [u’a0’, u’a1’, u’a2’, u’a3’, u’a4’, u’a5’, u’a6’, u’b0’, u’b1’, u’b2’, u’b3’, u’b4’, u’b5’, u’b6’, u’legal’, u’letter’]
"paperSize": "a4",
// How to mark detected chapters.
// Choices are “pagebreak”, “rule”, "both" or “none”.
"chapterMark" : "pagebreak",
// An XPath expression. Page breaks are inserted before the specified elements.
// To disable use the expression: "/"
"pageBreaksBefore": "/",
// Margin (in pts)
// Note: 72 pts equals 1 inch
"margin": {
+3 -3
View File
@@ -18,7 +18,7 @@ var Generator = function(book, format) {
this.namespace = "ebook";
// Styles to use
this.styles = _.compact(["ebook", this.ebookFormat]);
this.styles = _.compact(["print", "ebook", this.ebookFormat]);
// Convert images (svg -> png)
this.convertImages = true;
@@ -68,8 +68,6 @@ Generator.prototype.finish = function() {
"--book-producer": "GitBook",
"--publisher": "GitBook",
"--chapter": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter ')]",
"--chapter-mark": "pagebreak",
"--page-breaks-before": "/",
"--level1-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-1 ')]",
"--level2-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-2 ')]",
"--level3-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-3 ')]",
@@ -82,6 +80,8 @@ Generator.prototype.finish = function() {
var pdfOptions = that.options.pdf;
_.extend(_options, {
"--chapter-mark": String(pdfOptions.chapterMark),
"--page-breaks-before": String(pdfOptions.pageBreaksBefore),
"--margin-left": String(pdfOptions.margin.left),
"--margin-right": String(pdfOptions.margin.right),
"--margin-top": String(pdfOptions.margin.top),
+10 -13
View File
@@ -4,9 +4,8 @@ var Q = require("q");
var _ = require("lodash");
var nunjucks = require("nunjucks");
var AutoEscapeExtension = require("nunjucks-autoescape");
var FilterExtension = require("nunjucks-filter");
var I18nExtension = require("nunjucks-i18n");
var AutoEscapeExtension = require("nunjucks-autoescape")(nunjucks);
var FilterExtension = require("nunjucks-filter")(nunjucks);
var fs = require("../utils/fs");
var BaseGenerator = require("../generator");
@@ -51,12 +50,13 @@ Generator.prototype.prepareStyles = function() {
this.styles = _.chain(this.styles)
.map(function(style) {
var stylePath = that.options.styles[style];
if (fs.existsSync(path.resolve(that.book.root, stylePath))) {
return stylePath;
if (stylePath && fs.existsSync(path.resolve(that.book.root, stylePath))) {
return [style, stylePath];
}
return null;
})
.compact()
.object()
.value();
return Q();
@@ -79,8 +79,8 @@ Generator.prototype.prepareTemplateEngine = function() {
.then(function() {
var language = that.book.config.normalizeLanguage();
if (!i18n.getByLanguage(language) && language != "en") {
that.book.log.warn.ln("Language '"+language+"' is not available as a layout locales (en, "+i18n.getLanguages().join(", ")+")");
if (!i18n.hasLocale(language)) {
that.book.log.warn.ln("Language '"+language+"' is not available as a layout locales (en, "+i18n.getLocales().join(", ")+")");
}
var folders = _.chain(that.templates)
@@ -105,11 +105,6 @@ Generator.prototype.prepareTemplateEngine = function() {
// Add extension
that.env.addExtension('AutoEscapeExtension', new AutoEscapeExtension(that.env));
that.env.addExtension('FilterExtension', new FilterExtension(that.env));
that.env.addExtension('I18nExtension', new I18nExtension({
env: that.env,
translations: i18n.getLocales(),
locale: "language"
}));
});
};
@@ -136,7 +131,7 @@ Generator.prototype.convertFile = function(input) {
}
})
.then(function(page) {
var relativeOutput = that.book.contentLink(page.path);
var relativeOutput = that.book.contentPath(page.path);
var output = path.join(that.options.output, relativeOutput);
var basePath = path.relative(path.dirname(output), that.options.output) || ".";
@@ -243,6 +238,8 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
basePath: ".",
staticBase: path.join(".", "gitbook"),
'__': that.book.i18n.bind(that.book)
}, options)
);
})
+1 -1
View File
@@ -155,7 +155,7 @@ Plugin.prototype.callHook = function(name, data) {
if (!hookFunc) return Q(data);
this.book.log.debug.ln("call hook", name);
if (!_.contains(Plugin.HOOKS, name)) this.book.log.warn.ln("hook '"+name+"' used by plugin '"+this.name+"' is deprecated, and will be remove in the coming versions");
if (!_.contains(Plugin.HOOKS, name)) this.book.log.warn.ln("hook '"+name+"' used by plugin '"+this.name+"' is deprecated, and will be removed in the coming versions");
return Q()
.then(function() {
+13 -4
View File
@@ -2,9 +2,9 @@ var _ = require("lodash");
var Q = require("q");
var path = require("path");
var nunjucks = require("nunjucks");
var escapeStringRegexp = require("escape-string-regexp");
var git = require("./utils/git");
var stringUtils = require("./utils/string");
var fs = require("./utils/fs");
var batch = require("./utils/batch");
var pkg = require("../package.json");
@@ -250,7 +250,7 @@ TemplateEngine.prototype.addBlock = function(name, block) {
// Extract main body and kwargs
var body = args.pop();
var kwargs = args.pop() || {};
var kwargs = _.isObject(_.last(args))? args.pop() : {};
// Extract blocks body
var _blocks = _.map(block.blocks, function(blockName, i){
@@ -302,7 +302,7 @@ TemplateEngine.prototype.addBlock = function(name, block) {
TemplateEngine.prototype._applyShortcut = function(parser, content, shortcut) {
if (!_.contains(shortcut.parsers, parser)) return content;
var regex = new RegExp(
stringUtils.escapeRegex(shortcut.start) + "([\\s\\S]*?[^\\$])" + stringUtils.escapeRegex(shortcut.end),
escapeStringRegexp(shortcut.start) + "([\\s\\S]*?[^\\$])" + escapeStringRegexp(shortcut.end),
'g'
);
return content.replace(regex, function(all, match) {
@@ -316,6 +316,9 @@ TemplateEngine.prototype.renderString = function(content, context, options) {
// Variables from book.json
book: this.book.options.variables,
// Complete book.json
config: this.book.options,
// infos about gitbook
gitbook: {
version: pkg.version,
@@ -331,7 +334,13 @@ TemplateEngine.prototype.renderString = function(content, context, options) {
// Replace shortcuts
content = _.reduce(this.shortcuts, _.partial(this._applyShortcut.bind(this), options.type), content);
return Q.nfcall(this.env.renderString.bind(this.env), content, context, options);
return Q.nfcall(this.env.renderString.bind(this.env), content, context, options)
.fail(function(err) {
if (_.isString(err)) err = new Error(err);
err.message = err.message.replace(/^Error: /, "");
throw err;
});
};
// Render a file from the book
+36
View File
@@ -0,0 +1,36 @@
var hljs = require('highlight.js');
var MAP = {
'py': 'python',
'js': 'javascript',
'json': 'javascript',
'rb': 'ruby',
'csharp': 'cs',
};
function normalize(lang) {
if(!lang) { return null; }
var lower = lang.toLowerCase();
return MAP[lower] || lower;
}
function highlight(lang, code) {
if(!lang) return code;
// Normalize lang
lang = normalize(lang);
try {
return hljs.highlight(lang, code).value;
} catch(e) { }
return code;
}
// Exports
module.exports = {
highlight: highlight,
normalize: normalize,
MAP: MAP
};
+40 -32
View File
@@ -2,32 +2,20 @@ var _ = require("lodash");
var path = require("path");
var fs = require("fs");
var I18N_PATH = path.resolve(__dirname, "../../theme/i18n/")
var i18n = require("i18n");
var getLocales = _.memoize(function() {
var locales = fs.readdirSync(I18N_PATH);
return _.chain(locales)
.map(function(local) {
local = path.basename(local, ".json");
return [local, _.extend({
direction: "ltr"
}, require(path.join(I18N_PATH, local)), {
id: local
})];
})
.object()
.value();
var I18N_PATH = path.resolve(__dirname, "../../theme/i18n/")
var DEFAULT_LANGUAGE = "en";
var LOCALES = _.map(fs.readdirSync(I18N_PATH), function(lang) {
return path.basename(lang, ".json");
});
var getLanguages = function() {
return _.keys(getLocales());
};
var getByLanguage = function(lang) {
lang = normalizeLanguage(lang);
var locales = getLocales();
return locales[lang];
};
i18n.configure({
locales: LOCALES,
directory: I18N_PATH,
defaultLocale: DEFAULT_LANGUAGE,
updateFiles: false
});
var compareLocales = function(lang, locale) {
var langMain = _.first(lang.split("-"));
@@ -43,12 +31,9 @@ var compareLocales = function(lang, locale) {
};
var normalizeLanguage = _.memoize(function(lang) {
var locales = getLocales();
var language = _.chain(locales)
var language = _.chain(LOCALES)
.values()
.map(function(locale) {
locale = locale.id;
return {
locale: locale,
score: compareLocales(lang, locale)
@@ -64,9 +49,32 @@ var normalizeLanguage = _.memoize(function(lang) {
return language || lang;
});
module.exports = {
getLocales: getLocales,
getLanguages: getLanguages,
getByLanguage: getByLanguage,
normalizeLanguage: normalizeLanguage
var translate = function(locale, phrase) {
var args = Array.prototype.slice.call(arguments, 2);
return i18n.__.apply({}, [{
locale: locale,
phrase: phrase
}].concat(args));
};
var getCatalog = function(locale) {
locale = normalizeLanguage(locale);
return i18n.getCatalog(locale);
};
var getLocales = function() {
return LOCALES;
};
var hasLocale = function(locale) {
return _.contains(LOCALES, locale);
}
module.exports = {
__: translate,
normalizeLanguage: normalizeLanguage,
getCatalog: getCatalog,
getLocales: getLocales,
hasLocale: hasLocale
};
+2 -1
View File
@@ -36,7 +36,7 @@ function navigation(summary, files) {
var prev = null, next = null;
// Skip if no path
if(!current.path) return null;
if(!current.exists) return null;
// Find prev
prev = _.chain(navNodes)
@@ -59,6 +59,7 @@ function navigation(summary, files) {
return [current.path, {
index: i,
title: current.title,
introduction: current.introduction,
prev: prev,
next: next,
level: current.level,
+35 -3
View File
@@ -11,6 +11,7 @@ var links = require('./links');
var imgUtils = require('./images');
var fs = require('./fs');
var batch = require('./batch');
var code = require('./code');
// Render a cheerio dom as html
var renderDom = function($, dom, options) {
@@ -183,6 +184,7 @@ function normalizeHtml(src, options) {
$(this).attr("src", src);
});
// Normalize links
$("a").each(function() {
var href = $(this).attr("href");
if (!href) return;
@@ -191,7 +193,7 @@ function normalizeHtml(src, options) {
// Keep it as it is
} else if (links.isRelative(href)) {
var parts = url.parse(href);
var absolutePath = path.join(options.base, parts.pathname);
var absolutePath = links.join(options.base, parts.pathname);
var anchor = parts.hash || "";
// If is in navigation relative: transform as content
@@ -210,12 +212,42 @@ function normalizeHtml(src, options) {
$(this).attr("href", href);
});
// Replace glossayr terms
_.each(options.glossary, function(term) {
// Highlight code blocks
$("code").each(function() {
// Extract language
var lang = _.chain(
($(this).attr("class") || "").split(" ")
)
.map(function(cl) {
if (cl.search("lang-") === 0) return cl.slice("lang-".length);
return null;
})
.compact()
.first()
.value();
if (lang) {
var html = code.highlight(
lang,
$(this).text()
);
$(this).html(html);
}
});
// Replace glossary terms
var glossary = _.sortBy(options.glossary, function(term) {
return -term.name.length;
});
_.each(glossary, function(term) {
var r = new RegExp( "\\b(" + pregQuote(term.name.toLowerCase()) + ")\\b" , 'gi' );
var includedInFiles = false;
$("*").each(function() {
// Ignore codeblocks
if (_.contains(["code", "pre", "a"], this.name.toLowerCase())) return;
replaceText($, this, r, function(match) {
// Add to files index in glossary
if (!includedInFiles) {
-5
View File
@@ -20,12 +20,7 @@ function optionsToShellArgs(options) {
.join(" ");
}
function escapeRegex(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
module.exports = {
escapeRegex: escapeRegex,
escapeShellArg: escapeShellArg,
optionsToShellArgs: optionsToShellArgs,
toLowerCase: String.prototype.toLowerCase.call.bind(String.prototype.toLowerCase)
+8 -7
View File
@@ -1,7 +1,7 @@
var Q = require('q');
var _ = require('lodash');
var path = require('path');
var Gaze = require('gaze').Gaze;
var chokidar = require('chokidar');
var parsers = require('gitbook-parsers')
@@ -17,17 +17,18 @@ function watch(dir) {
toWatch.push("**/*"+ext);
});
var gaze = new Gaze(toWatch, {
cwd: dir
var watcher = chokidar.watch(toWatch, {
cwd: dir,
ignoreInitial: true
});
gaze.once("all", function(e, filepath) {
gaze.close();
watcher.once("all", function(e, filepath) {
watcher.close();
d.resolve(filepath);
});
gaze.once("error", function(err) {
gaze.close();
watcher.once("error", function(err) {
watcher.close();
d.reject(err);
});
+14 -11
View File
@@ -1,6 +1,6 @@
{
"name": "gitbook",
"version": "2.0.0-beta.5",
"version": "2.2.0",
"homepage": "https://www.gitbook.com",
"description": "Library and cmd utility to generate GitBooks",
"main": "lib/index.js",
@@ -12,16 +12,16 @@
"resolve": "0.6.3",
"fs-extra": "0.16.5",
"fstream-ignore": "1.0.2",
"gitbook-parsers": "0.5.4",
"nunjucks": "git+https://github.com/mozilla/nunjucks.git#0f8b21b8df7e8e852b2e1889388653b7075f0d09",
"nunjucks-autoescape": "0.1.1",
"nunjucks-filter": "0.1.0",
"nunjucks-i18n": "1.0.0",
"gitbook-parsers": "0.7.6",
"nunjucks": "mozilla/nunjucks#103513c294835bcbe64222de6db494e2555e294e",
"nunjucks-autoescape": "1.0.0",
"nunjucks-filter": "1.0.0",
"i18n": "0.5.0",
"semver": "2.2.1",
"npmi": "0.1.1",
"cheerio": "0.18.0",
"cheerio": "0.19.0",
"gitbook-plugin-livereload": "0.0.1",
"gaze": "~0.5.1",
"chokidar": "~1.0.1",
"send": "0.2.0",
"tiny-lr": "0.1.5",
"tmp": "0.0.24",
@@ -31,10 +31,13 @@
"request": "2.51.0",
"npm": "2.4.1",
"dom-serializer": "0.1.0",
"spawn-cmd": "0.0.2"
"spawn-cmd": "0.0.2",
"highlight.js": "8.6.0",
"escape-string-regexp": "1.0.3"
},
"devDependencies": {
"mocha": "2.2.1",
"should": "5.2.0",
"grunt": "~0.4.2",
"grunt-cli": "0.1.11",
"grunt-contrib-copy": "0.5.0",
@@ -43,7 +46,7 @@
"grunt-bower-install-simple": "0.9.2"
},
"scripts": {
"test": "node_modules/.bin/mocha --reporter list --timeout 15000"
"test": "node_modules/.bin/mocha --reporter spec --timeout 15000"
},
"repository": {
"type": "git",
@@ -58,7 +61,7 @@
"gitbook"
],
"author": "FriendCode Inc. <contact@gitbook.com>",
"license": "Apache 2",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/GitbookIO/gitbook/issues"
},
+54
View File
@@ -0,0 +1,54 @@
var _ = require('lodash');
var fs = require('fs');
var path = require('path');
var should = require('should');
var cheerio = require('cheerio');
should.Assertion.add('file', function(file, description) {
this.params = { actual: this.obj.toString(), operator: 'have file ' + file, message: description };
this.obj.should.have.property('options').which.is.an.Object;
this.obj.options.should.have.property('output').which.is.a.String;
this.assert(fs.existsSync(path.resolve(this.obj.options.output, file)));
});
should.Assertion.add('jsonfile', function(file, description) {
this.params = { actual: this.obj.toString(), operator: 'have valid jsonfile ' + file, message: description };
this.obj.should.have.property('options').which.is.an.Object;
this.obj.options.should.have.property('output').which.is.a.String;
this.assert(JSON.parse(fs.readFileSync(path.resolve(this.obj.options.output, file), { encoding: "utf-8" })));
});
should.Assertion.add('html', function(rules, description) {
this.params = { actual: "HTML string", operator: 'valid html', message: description };
var $ = cheerio.load(this.obj);
_.each(rules, function(validations, query) {
validations = _.defaults(validations || {}, {
count: 1,
attributes: {},
trim: false,
text: undefined
});
var $el = $(query);
// Test number of elements
$el.length.should.be.equal(validations.count);
// Test text
if (validations.text !== undefined) {
var text = $el.text();
if (validations.trim) text = text.trim();
text.should.be.equal(validations.text);
}
// Test attributes
_.each(validations.attributes, function(value, name) {
var attr = $el.attr(name);
should(attr).be.ok;
attr.should.be.equal(value);
});
});
});
+3
View File
@@ -0,0 +1,3 @@
# Readme
Default description for the book.
+1
View File
@@ -0,0 +1 @@
# Readme
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
"title": "js-config"
};
+1
View File
@@ -0,0 +1 @@
# Readme
+3
View File
@@ -0,0 +1,3 @@
{
"title": "json-config"
}
+10
View File
@@ -0,0 +1,10 @@
# Readme
### Relative
<p id="t1">{% include "./hello.md" %}</p>
### Git
<p id="t2">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md" %}</p>
<p id="t3">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test2.md" %}</p>
+1
View File
@@ -0,0 +1 @@
# Summary
+1
View File
@@ -0,0 +1 @@
Hello World
+13
View File
@@ -0,0 +1,13 @@
# Glossary
## test
Just a simple and easy to understand test.
## hakunamatata
This word is not present in the content.
## test long
This is a test with a longer text that the first entry to test order.
+10
View File
@@ -0,0 +1,10 @@
# Readme
The word test should be replaced by a glossary link.
```
But the word test should not be replaced in code blocks
```
The two words test long should be replaced by the correct glossary links.
+3
View File
@@ -0,0 +1,3 @@
# Summary
* [Page](folder/PAGE.md)
+3
View File
@@ -0,0 +1,3 @@
# Page
Just a page in a sub-directory to test relative link to glossary.
+18
View File
@@ -0,0 +1,18 @@
# Tests for Images
# SVG relative image
### Convert SVG to PNG
![test.svg](test.svg)
### Should only convert it once
![test.svg](test.svg)
### Download remote images
![remote image](http://upload.wikimedia.org/wikipedia/commons/b/b0/NewTux.svg)
### Remote images with same filename
![youtube1](http://img.youtube.com/vi/9bZkp7q19f0/0.jpg)
![youtube2](http://img.youtube.com/vi/IkV2HQLAKHY/0.jpg)
+3
View File
@@ -0,0 +1,3 @@
# Summary
* [Page](./folder/PAGE.md)
+3
View File
@@ -0,0 +1,3 @@
### Images from other folder
![test.svg](../test.svg)

Before

Width:  |  Height:  |  Size: 730 B

After

Width:  |  Height:  |  Size: 730 B

+3
View File
@@ -0,0 +1,3 @@
*
!SUMMARY.md
!.gitignore
+6
View File
@@ -0,0 +1,6 @@
# Summary
* [Hello](hello.md)
* [Hello 2](hello2.md)
* Hello 3
* [Hello 4](hello3/hello4.md)
+4
View File
@@ -0,0 +1,4 @@
# Language
* [English](en)
* [French](fr)
+1
View File
@@ -0,0 +1 @@
# Readme
+1
View File
@@ -0,0 +1 @@
Hello
+1
View File
@@ -0,0 +1 @@
# Summary
+1
View File
@@ -0,0 +1 @@
Bonjour
+1
View File
@@ -0,0 +1 @@
# Summary
+1
View File
@@ -0,0 +1 @@
# Readme
+4
View File
@@ -0,0 +1,4 @@
# Summary
* [Folder1](folder1/README.md)
* [Folder2](folder2/README.md)
+3
View File
@@ -0,0 +1,3 @@
# Folder 1
[Link to folder2](../folder2/README.md)
+1
View File
@@ -0,0 +1 @@
# Folder 2
+3
View File
@@ -0,0 +1,3 @@
# Readme
Default description for the book.
+1
View File
@@ -0,0 +1 @@
# Summary
+3
View File
@@ -0,0 +1,3 @@
body {
color: red;
}
+1
View File
@@ -0,0 +1 @@
# Page 1
+1
View File
@@ -0,0 +1 @@
# Readme
+5
View File
@@ -0,0 +1,5 @@
# Summary
* [Page 1](./PAGE1.md)
* [Page 2](./folder/PAGE2.md)
* [Don't exists](./NOTFOUND.md)
+1
View File
@@ -0,0 +1 @@
# Page 2
+26 -8
View File
@@ -1,14 +1,32 @@
var fs = require('fs');
var path = require('path');
var assert = require('assert');
var Book = require('../').Book;
describe('Configuration parsing', function () {
it('should correctly load from json', function() {
assert(books[0].options.title == "Test");
describe('Configuration', function () {
it('should extract default title from README', function() {
return books.parse("basic")
.then(function(book) {
book.options.title.should.be.equal("Readme");
});
});
it('should correctly load from javascript', function() {
assert(books[4].options.title == "Test 2");
it('should extract default description from README', function() {
return books.parse("basic")
.then(function(book) {
book.options.description.should.be.equal("Default description for the book.");
});
});
it('should correctly load from json (book.json)', function() {
return books.parse("config-json")
.then(function(book) {
book.options.title.should.be.equal("json-config");
});
});
it('should correctly load from JavaScript (book.js)', function() {
return books.parse("config-js")
.then(function(book) {
book.options.title.should.be.equal("js-config");
});
});
});
+43
View File
@@ -0,0 +1,43 @@
var fs = require('fs');
var path = require('path');
describe('ConRefs', function () {
var book, readme;
before(function() {
return books.generate("conrefs", "website")
.then(function(_book) {
book = _book;
readme = fs.readFileSync(
path.join(book.options.output, "index.html"),
{ encoding: "utf-8" }
);
});
});
it('should handle local references', function() {
readme.should.be.html({
".page-inner p#t1": {
count: 1,
text: "Hello World",
trim: true
}
});
});
it('should handle git references', function() {
readme.should.be.html({
".page-inner p#t2": {
count: 1,
text: "Hello from git",
trim: true
},
".page-inner p#t3": {
count: 1,
text: "First Hello. Hello from git",
trim: true
}
});
});
});
+46 -27
View File
@@ -1,36 +1,55 @@
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var assert = require('assert');
var cheerio = require('cheerio');
var fs = require("fs");
var fsUtil = require("../lib/utils/fs");
describe('eBook generator', function () {
describe('Basic Book', function() {
var book;
before(function() {
return books.generate("basic", "ebook")
.then(function(_book) {
book = _book;
});
});
describe('eBook Generator', function () {
it('should correctly generate ebook pages', function(done) {
testGeneration(books[1], "ebook", function(output) {
assert(fs.existsSync(path.join(output, "SUMMARY.html")));
}, done);
it('should correctly output a SUMMARY.html', function() {
book.should.have.file("SUMMARY.html");
});
it('should correctly copy assets', function() {
book.should.have.file("gitbook");
book.should.have.file("gitbook/style.css");
});
});
it('should correctly convert svg images to png', function(done) {
testGeneration(books[4], "ebook", function(output) {
// Check that all images exists
_.each([
"index.html",
"sub/PAGE.html"
], function(pageName) {
var pageFile = path.join(output, pageName);
var pageFolder = path.dirname(pageFile);
var pageContent = fs.readFileSync(pageFile, {encoding: "utf8"});
var $ = cheerio.load(pageContent);
describe('Custom styles', function() {
var book;
$("img").each(function() {
var src = $(this).attr("src");
assert(fs.existsSync(path.resolve(pageFolder, src)), src+" not found for page "+pageName);
})
before(function() {
return books.generate("style-print", "ebook")
.then(function(_book) {
book = _book;
});
});
it('should remove default print.css', function() {
var PAGE = fs.readFileSync(
path.join(book.options.output, "index.html"),
{ encoding: "utf-8" }
);
PAGE.should.be.html({
"link": {
count: 1,
attributes: {
href: "./styles/print.css"
}
}
});
}, done);
});
});
it('should correctly print.css', function() {
book.should.have.file("styles");
book.should.have.file("styles/print.css");
});
})
});
-6
View File
@@ -1,6 +0,0 @@
# Glossary
## Description
This is the dexcription of a description.
-3
View File
@@ -1,3 +0,0 @@
# Other Title
This is a description, it's simple, no ?
-1
View File
@@ -1 +0,0 @@
# Summary
-3
View File
@@ -1,3 +0,0 @@
{
"title": "Test"
}
-3
View File
@@ -1,3 +0,0 @@
body {
background: red;
}
-9
View File
@@ -1,9 +0,0 @@
# Test
a test text
# Test 2
a second test
-4
View File
@@ -1,4 +0,0 @@
# Test
This file is not parsed by gitbook because the structure is defined in book.json.
-6
View File
@@ -1,6 +0,0 @@
# Summary
* [Chapter 1](test.md)
* [Article 1](sub/test1.md)
* [Chapter 2](test2.md)
* [Google](https://www.google.com)
-5
View File
@@ -1,5 +0,0 @@
{
"structure": {
"readme": "intro.md"
}
}
-4
View File
@@ -1,4 +0,0 @@
# My Book
Test description
-8
View File
@@ -1,8 +0,0 @@
# This file is used to etst links transformations:
This is a relative link [test](../intro.md).
![image](../image.png)
This is a glossary link.
-4
View File
@@ -1,4 +0,0 @@
# Languages
* [English](en/)
* [French](fr/)
-2
View File
@@ -1,2 +0,0 @@
# Multi-Languages test
-1
View File
@@ -1 +0,0 @@
# English Book
-1
View File
@@ -1 +0,0 @@
# French Book
-4
View File
@@ -1,4 +0,0 @@
= My Book
Test description
-5
View File
@@ -1,5 +0,0 @@
= Summary
. link:test.adoc[Chapter 1]
.. link:test1.adoc[Article 1]
. link:test2.adoc[Chapter 2]
View File
View File
View File
-11
View File
@@ -1,11 +0,0 @@
# Other Title
A description
![test image to be converted](./test.svg)
![test url](http://upload.wikimedia.org/wikipedia/commons/b/b0/NewTux.svg)
![test image to be converted, second use](./test.svg)
# Test with youtube videos that have the same filename:
![youtube1](http://img.youtube.com/vi/9bZkp7q19f0/0.jpg)
![youtube2](http://img.youtube.com/vi/IkV2HQLAKHY/0.jpg)
-3
View File
@@ -1,3 +0,0 @@
# Summary
* [Page](sub/PAGE.md)
-3
View File
@@ -1,3 +0,0 @@
module.exports = {
title: "Test 2"
};
-18
View File
@@ -1,18 +0,0 @@
##
## Image from root page
![test image to be converted](../test.svg)
![test url](http://upload.wikimedia.org/wikipedia/commons/b/b0/NewTux.svg)
## Inline svg
{% html %}
<svg xmlns="http://www.w3.org/2000/svg">
<path d="M97.008198003228,6.103238498249268A97.2,97.2 0 0,1 87.9491894996971,41.385747140125076L54.289623147961166,25.546757493904366A60,60 0 0,0 59.88160370569629,3.7674311717588074Z"></path>
</svg>
{% endhtml %}
## Remote image
![remote url](http://upload.wikimedia.org/wikipedia/commons/a/af/Tux.png)
-5
View File
@@ -1,5 +0,0 @@
# Test Content inclusion
{% include "./test.md" %}
Git1:{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md" %}
Git2:{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test2.md" %}
-1
View File
@@ -1 +0,0 @@
Hello World
+17 -18
View File
@@ -1,32 +1,31 @@
var path = require('path');
var _ = require('lodash');
var assert = require('assert');
var fs = require("fs");
var git = require("../lib/utils/git");
var should = require('should');
var git = require('../lib/utils/git');
describe('GIT parser and getter', function () {
it('should correctly parse an https url', function() {
var parts = git.parseUrl("git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md");
assert(parts);
assert.equal(parts.host, "https://gist.github.com/69ea4542e4c8967d2fa7.git");
assert.equal(parts.ref, "master");
assert.equal(parts.filepath, "test.md");
should.exist(parts);
parts.host.should.be.equal("https://gist.github.com/69ea4542e4c8967d2fa7.git");
parts.ref.should.be.equal("master");
parts.filepath.should.be.equal("test.md");
});
it('should correctly parse an https url with a reference', function() {
var parts = git.parseUrl("git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md#0.1.2");
assert(parts);
assert.equal(parts.host, "https://gist.github.com/69ea4542e4c8967d2fa7.git");
assert.equal(parts.ref, "0.1.2");
assert.equal(parts.filepath, "test.md");
should.exist(parts);
parts.host.should.be.equal("https://gist.github.com/69ea4542e4c8967d2fa7.git");
parts.ref.should.be.equal("0.1.2");
parts.filepath.should.be.equal("test.md");
});
it('should correctly parse an ssh url', function() {
var parts = git.parseUrl("git+git@github.com:GitbookIO/gitbook.git/directory/README.md#e1594cde2c32e4ff48f6c4eff3d3d461743d74e1");
assert(parts);
assert.equal(parts.host, "git@github.com:GitbookIO/gitbook.git");
assert.equal(parts.ref, "e1594cde2c32e4ff48f6c4eff3d3d461743d74e1");
assert.equal(parts.filepath, "directory/README.md");
should.exist(parts);
parts.host.should.be.equal("git@github.com:GitbookIO/gitbook.git");
parts.ref.should.be.equal("e1594cde2c32e4ff48f6c4eff3d3d461743d74e1");
parts.filepath.should.be.equal("directory/README.md");
});
});
+78 -26
View File
@@ -1,35 +1,87 @@
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var assert = require('assert');
var cheerio = require('cheerio');
var fs = require("fs");
var fsUtil = require("../lib/utils/fs");
describe('Glossary', function () {
describe('Parsing', function() {
var book;
before(function() {
return books.parse("glossary")
.then(function(_book) {
book = _book;
});
});
describe('Glossary Generation', function () {
it('should correctly replace glossary terms', function(done) {
testGeneration(books[0], "website", function(output) {
var content = fs.readFileSync(path.join(output, "index.html"), { encoding: "utf8" });
var $ = cheerio.load(content);
var $body = $(".page-inner");
var $a = $("a[href='GLOSSARY.html#description']");
assert($a.length == 1);
assert($a.text() == "description");
}, done);
it('should correctly list items', function() {
book.should.have.property("glossary");
book.glossary.should.have.lengthOf(3);
});
});
it('should correctly replace glossary terms in sub pages', function(done) {
testGeneration(books[1], "website", function(output) {
var content = fs.readFileSync(path.join(output, "sub/test1.html"), { encoding: "utf8" });
var $ = cheerio.load(content);
describe('Generation', function() {
var book;
var $body = $(".page-inner");
var $a = $("a[href='../GLOSSARY.html#test']");
assert($a.length == 1);
assert($a.text() == "test");
assert($a.attr("title") == "a test text");
}, done);
before(function() {
return books.generate("glossary", "website")
.then(function(_book) {
book = _book;
});
});
it('should correctly generate a GLOSSARY.html', function() {
book.should.have.file("GLOSSARY.html");
});
describe('Page Integration', function() {
var readme, page;
before(function() {
readme = fs.readFileSync(
path.join(book.options.output, "index.html"),
{ encoding: "utf-8" }
);
page = fs.readFileSync(
path.join(book.options.output, "folder/PAGE.html"),
{ encoding: "utf-8" }
);
});
it('should correctly replaced terms by links', function() {
readme.should.be.html({
".page-inner a[href='GLOSSARY.html#test']": {
count: 1,
text: "test",
attributes: {
title: "Just a simple and easy to understand test."
}
}
});
});
it('should correctly replaced terms by links (relative)', function() {
page.should.be.html({
".page-inner a[href='../GLOSSARY.html#test']": {
count: 1
}
});
});
it('should not replace terms in codeblocks', function() {
readme.should.be.html({
".page-inner code a": {
count: 0
}
});
});
it('should correctly select the longest term', function() {
readme.should.be.html({
".page-inner a[href='GLOSSARY.html#test_long']": {
count: 1,
text: "test long"
}
});
});
});
});
});
+55 -49
View File
@@ -1,62 +1,68 @@
var os = require('os');
var path = require('path');
var Q = require('q');
var fs = require('fs');
var _ = require('lodash');
var should = require('should');
var fsUtil = require("../lib/utils/fs");
var fsUtil = require('../lib/utils/fs');
var Book = require('../').Book;
var LOG_LEVELS = require('../').LOG_LEVELS;
// Nicety for mocha / Q
global.qdone = function qdone(promise, done) {
return promise.then(function() {
return done();
}, function(err) {
return done(err);
}).done();
require("./assertions");
var BOOKS = {};
var TMPDIR = os.tmpdir();
// Generate and return a book
function generateBook(bookId, test) {
return parseBook(bookId, test)
.then(function(book) {
return book.generate(test)
.thenResolve(book);
});
}
// Generate and return a book
function parseBook(bookId, test) {
test = test || "website";
BOOKS[bookId] = BOOKS[bookId] || {};
if (BOOKS[bookId][test]) return Q(BOOKS[bookId][test]);
BOOKS[bookId][test] = new Book(path.resolve(__dirname, "books", bookId), {
logLevel: LOG_LEVELS.DISABLED,
config: {
output: path.resolve(TMPDIR, bookId+"-"+test)
}
});
return BOOKS[bookId][test].parse()
.then(function() {
return BOOKS[bookId][test];
});
}
global.books = {
parse: parseBook,
generate: generateBook
};
// Test generation of a book
global.testGeneration = function(book, type, func, done) {
var OUTPUT_PATH = book.options.output;
qdone(
book.generate(type)
.then(function() {
func(OUTPUT_PATH);
})
.fin(function() {
return fsUtil.remove(OUTPUT_PATH);
}),
done);
};
// Books for testings
var books = fs.readdirSync(path.join(__dirname, './fixtures/'));
global.books = _.chain(books)
.sortBy()
.map(function(book) {
if (book.indexOf("test") !== 0) return null;
return new Book(path.join(__dirname, './fixtures/', book), {
logLevel: LOG_LEVELS.DISABLED
});
})
.compact()
.value();
// Init before doing tests
before(function(done) {
qdone(
_.reduce(global.books, function(prev, book) {
// Cleanup all tests
after(function() {
return _.chain(BOOKS)
.map(function(types, bookId) {
return _.values(types);
})
.flatten()
.reduce(function(prev, book) {
return prev.then(function() {
return fsUtil.remove(path.join(book.root, "_book"));
return fsUtil.remove(book.options.output);
})
.then(function() {
return book.parse();
});
}, Q()),
done
);
}, Q())
.value();
});
+56
View File
@@ -0,0 +1,56 @@
var fs = require('fs');
var _ = require('lodash');
var path = require('path');
var cheerio = require('cheerio');
describe('Images', function () {
var book, readme, $, $img, srcs;
before(function() {
return books.generate("images", "ebook")
.then(function(_book) {
book = _book;
readme = fs.readFileSync(
path.join(book.options.output, "index.html"),
{ encoding: "utf-8" }
);
$ = cheerio.load(readme);
$img = $("img");
srcs = $img.map(function() { return $(this).attr("src"); })
});
});
it('should detect all images', function() {
_.uniq(srcs).should.have.lengthOf(4);
});
it('should keep image tags', function() {
srcs.should.have.lengthOf(5);
});
it('should not have .svg files', function() {
_.each(srcs, function(src) {
path.extname(src).should.not.equal(".svg");
});
});
it('should correctly convert svg images to png', function() {
_.each(srcs, function(src) {
book.should.have.file(src);
});
});
it('should handle relative paths', function() {
var PAGE = fs.readFileSync(
path.join(book.options.output, "folder/PAGE.html"),
{ encoding: "utf-8" }
);
PAGE.should.be.html({
"img[src='../test.png']": {
count: 1
}
});
});
});
+23
View File
@@ -0,0 +1,23 @@
var fs = require('fs');
var path = require('path');
var should = require('should');
var Book = require('../').Book;
var LOG_LEVELS = require('../').LOG_LEVELS;
describe('Init Books', function () {
var initRoot;
before(function() {
initRoot = path.resolve(__dirname, "books/init");
return Book.init(initRoot, {
logLevel: LOG_LEVELS.DISABLED,
});
});
it('should create all chapters', function() {
should(fs.existsSync(path.resolve(initRoot, "hello.md"))).be.ok;
should(fs.existsSync(path.resolve(initRoot, "hello2.md"))).be.ok;
should(fs.existsSync(path.resolve(initRoot, "hello3/hello4.md"))).be.ok;
});
});
+79 -46
View File
@@ -1,59 +1,92 @@
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var assert = require('assert');
var fs = require("fs");
describe('JSON generator', function () {
it('should correctly generate a book to json with glossary', function(done) {
testGeneration(books[0], "json", function(output) {
assert(fs.existsSync(path.join(output, "README.json")));
describe('Basic Book', function() {
var book;
var readme = JSON.parse(fs.readFileSync(path.join(output, "README.json")));
assert(readme.sections[0].content.indexOf('class="glossary-term"') > 0);
}, done);
before(function() {
return books.generate("basic", "json")
.then(function(_book) {
book = _book;
});
});
it('should correctly output a README.json', function() {
book.should.have.file("README.json");
});
it('should output a valid json', function() {
book.should.have.jsonfile("README.json");
});
describe('Page Format', function() {
var page;
before(function() {
page = JSON.parse(
fs.readFileSync(
path.join(book.options.output, "README.json"),
{ encoding: "utf-8" }
)
);
});
it('should contains valid section', function() {
page.should.have.property("sections").with.lengthOf(1);
page.sections[0].should.have.property("content").which.is.a.String;
page.sections[0].should.have.property("type").which.is.a.String.which.equal("normal");
});
it('should contains valid progress', function() {
page.should.have.property("progress");
page.progress.should.have.property("chapters").with.lengthOf(1);
page.progress.should.have.property("current");
});
it('should contains no languages', function() {
page.should.have.property("langs").with.lengthOf(0);
});
});
});
it('should correctly generate a book to json with sub folders', function(done) {
testGeneration(books[1], "json", function(output) {
assert(fs.existsSync(path.join(output, "README.json")));
assert(fs.existsSync(path.join(output, "intro.json")));
assert(fs.existsSync(path.join(output, "sub/test1.json")));
describe('Multilingual Book', function() {
var book;
var test1 = JSON.parse(fs.readFileSync(path.join(output, "sub/test1.json")));
assert(test1.sections[0].content.indexOf("index.html") > 0);
}, done);
});
before(function() {
return books.generate("languages", "json")
.then(function(_book) {
book = _book;
});
});
it('should correctly generate a multilingual book to json', function(done) {
testGeneration(books[2], "json", function(output) {
assert(fs.existsSync(path.join(output, "README.json")));
assert(fs.existsSync(path.join(output, "en/README.json")));
assert(fs.existsSync(path.join(output, "fr/README.json")));
}, done);
});
it('should correctly output READMEs', function() {
book.should.have.file("README.json");
book.should.have.file("en/README.json");
book.should.have.file("fr/README.json");
});
it('should correctly generate an asciidoc book to json', function(done) {
testGeneration(books[3], "json", function(output) {
assert(fs.existsSync(path.join(output, "README.json")));
assert(fs.existsSync(path.join(output, "test.json")));
assert(fs.existsSync(path.join(output, "test1.json")));
assert(fs.existsSync(path.join(output, "test2.json")));
}, done);
});
it('should output valid json', function() {
book.should.have.jsonfile("README.json");
book.should.have.jsonfile("en/README.json");
book.should.have.jsonfile("fr/README.json");
});
it('should correctly generate a book with local inclusion', function(done) {
testGeneration(books[5], "json", function(output) {
var readme = JSON.parse(fs.readFileSync(path.join(output, "README.json")));
assert(readme.sections[0].content.indexOf('Hello World') > 0);
}, done);
});
describe('Page Format', function() {
var page;
it('should correctly generate a book with external inclusion', function(done) {
testGeneration(books[5], "json", function(output) {
var readme = JSON.parse(fs.readFileSync(path.join(output, "README.json")));
assert(readme.sections[0].content.indexOf('Git1:Hello from git') > 0);
assert(readme.sections[0].content.indexOf('Git2:First Hello. Hello from git') > 0);
}, done);
before(function() {
page = JSON.parse(
fs.readFileSync(
path.join(book.options.output, "README.json"),
{ encoding: "utf-8" }
)
);
});
it('should contains no languages', function() {
page.should.have.property("langs").with.lengthOf(2);
});
});
});
});
+40
View File
@@ -0,0 +1,40 @@
var fs = require('fs');
var path = require('path');
describe('Languages', function () {
describe('Parsing', function() {
var book;
before(function() {
return books.parse("languages")
.then(function(_book) {
book = _book;
});
});
it('should correctly list languages', function() {
book.should.have.property("books");
book.books.should.have.lengthOf(2);
book.books[0].options.language.should.be.equal("en");
book.books[1].options.language.should.be.equal("fr");
});
});
describe('Generation', function() {
var book;
before(function() {
return books.generate("languages", "website")
.then(function(_book) {
book = _book;
});
});
it('should correctly create books', function() {
book.should.have.file("index.html");
book.should.have.file("en/index.html");
book.should.have.file("fr/index.html");
});
});
});
+44 -19
View File
@@ -1,40 +1,65 @@
var path = require('path');
var should = require('should');
var fs = require('fs');
var _ = require('lodash');
var assert = require('assert');
var path = require('path');
var cheerio = require('cheerio');
var links = require("../lib/utils/links");
describe('Links', function () {
it('should correctly test external links', function() {
assert(links.isExternal("http://google.fr"));
assert(links.isExternal("https://google.fr"));
assert(!links.isExternal("test.md"));
assert(!links.isExternal("folder/test.md"));
assert(!links.isExternal("/folder/test.md"));
links.isExternal("http://google.fr").should.be.exactly(true);
links.isExternal("https://google.fr").should.be.exactly(true);
links.isExternal("test.md").should.be.exactly(false);
links.isExternal("folder/test.md").should.be.exactly(false);
links.isExternal("/folder/test.md").should.be.exactly(false);
});
it('should correctly test anchor links', function() {
assert(links.isAnchor("#test"));
assert(links.isAnchor(" #test"));
assert(!links.isAnchor("https://google.fr#test"));
assert(!links.isAnchor("test.md#test"));
it('should correctly detect anchor links', function() {
links.isAnchor("#test").should.be.exactly(true);
links.isAnchor(" #test").should.be.exactly(true);
links.isAnchor("https://google.fr#test").should.be.exactly(false);
links.isAnchor("test.md#test").should.be.exactly(false);
});
describe('toAbsolute', function() {
it('should correctly transform as absolute', function() {
assert.equal(links.toAbsolute("http://google.fr"), "http://google.fr");
assert.equal(links.toAbsolute("test.md", "./", "./"), "test.md");
assert.equal(links.toAbsolute("folder/test.md", "./", "./"), "folder/test.md");
links.toAbsolute("http://google.fr").should.be.equal("http://google.fr");
links.toAbsolute("test.md", "./", "./").should.be.equal("test.md");
links.toAbsolute("folder/test.md", "./", "./").should.be.equal("folder/test.md");
});
it('should correctly handle windows path', function() {
assert.equal(links.toAbsolute("folder\\test.md", "./", "./"), "folder/test.md");
links.toAbsolute("folder\\test.md", "./", "./").should.be.equal("folder/test.md");
});
it('should correctly handle absolute path', function() {
assert.equal(links.toAbsolute("/test.md", "./", "./"), "test.md");
assert.equal(links.toAbsolute("/test.md", "test", "test"), "../test.md");
assert.equal(links.toAbsolute("/sub/test.md", "test", "test"), "../sub/test.md");
links.toAbsolute("/test.md", "./", "./").should.be.equal("test.md");
links.toAbsolute("/test.md", "test", "test").should.be.equal("../test.md");
links.toAbsolute("/sub/test.md", "test", "test").should.be.equal("../sub/test.md");
});
});
describe('page', function() {
var book;
before(function() {
return books.generate("links", "website")
.then(function(_book) {
book = _book;
});
});
it('should correctly replace relative links', function() {
var readme = fs.readFileSync(
path.join(book.options.output, "folder1/index.html"),
{ encoding: "utf-8" }
);
var $ = cheerio.load(readme);
var $a = $(".page-inner a");
$a.attr('href').should.be.exactly("../folder2/index.html");
})
});
});
-82
View File
@@ -1,82 +0,0 @@
var path = require('path');
var _ = require('lodash');
var assert = require('assert');
describe('Book parsing', function () {
it('should correctly parse the readme', function() {
assert.equal(books[1].options.title, 'My Book');
assert.equal(books[1].options.description, 'Test description');
});
it('should correctly parse the readme with asciidoc', function() {
assert.equal(books[3].options.title, 'My Book');
assert.equal(books[3].options.description, 'Test description');
});
it('should correctly parse the summary', function() {
var LEXED = books[1].summary;
assert.equal(LEXED.chapters[0].path, 'intro.md');
assert.equal(LEXED.chapters[0].exists, true);
assert.equal(LEXED.chapters[0].introduction, true);
assert.equal(LEXED.chapters[0].external, false);
assert.equal(LEXED.chapters[1].path, 'test.md');
assert.equal(LEXED.chapters[1].exists, false);
assert.equal(LEXED.chapters[1].introduction, false);
assert.equal(LEXED.chapters[1].external, false);
assert.equal(LEXED.chapters[2].path, 'test2.md');
assert.equal(LEXED.chapters[2].exists, false);
assert.equal(LEXED.chapters[2].introduction, false);
assert.equal(LEXED.chapters[2].external, false);
assert.equal(LEXED.chapters[3].path, 'https://www.google.com');
assert.equal(LEXED.chapters[3].exists, true);
assert.equal(LEXED.chapters[3].introduction, false);
assert.equal(LEXED.chapters[3].external, true);
});
it('should correctly parse the glossary', function() {
var LEXED = books[1].glossary;
assert.equal(LEXED[0].id, "test");
assert.equal(LEXED[0].name, "Test");
assert.equal(LEXED[0].description, "a test text");
assert.equal(LEXED[1].id, "test_2");
assert.equal(LEXED[1].name, "Test 2");
assert.equal(LEXED[1].description, "a second test");
});
it('should correctly parse list of files', function() {
var FILES = books[1].files;
assert.deepEqual(FILES, [ 'README.md', 'intro.md', 'sub/', 'sub/test1.md' ]);
});
it('should correctly parse the languages', function() {
assert.equal(books[2].books.length, 2);
assert(books[2].isMultilingual());
assert.equal(books[2].books[0].options.language, "en");
assert.equal(books[2].books[0].options.title, "English Book");
assert.equal(books[2].books[1].options.language, "fr");
assert.equal(books[2].books[1].options.title, "French Book");
});
it('should correctly parse the navigation', function() {
var NAVIGATION = books[1].navigation;
assert.equal(_.size(NAVIGATION), 2);
assert(NAVIGATION["intro.md"])
assert.equal(NAVIGATION["intro.md"].title, "");
assert.equal(NAVIGATION["intro.md"].prev, null);
assert.equal(NAVIGATION["intro.md"].next.title, "Article 1");
assert.equal(NAVIGATION["sub/test1.md"].title, "Article 1");
assert.equal(NAVIGATION["sub/test1.md"].prev.title, "");
assert.equal(NAVIGATION["sub/test1.md"].next, null);
});
});
+147 -132
View File
@@ -1,189 +1,204 @@
var path = require('path');
var _ = require('lodash');
var assert = require('assert');
var fs = require("fs");
var should = require('should');
var path = require('path');
var Plugin = require("../lib/plugin");
var PLUGINS_ROOT = path.resolve(__dirname, "plugins");
var Plugin = require('../lib/plugin');
var PLUGINS_ROOT = path.resolve(__dirname, 'plugins');
describe('Plugins', function () {
describe('invalid plugin', function() {
it('should signal as invalid', function() {
var plugin = new Plugin(books[0], "invalid");
var book;
before(function() {
return books.parse("basic")
.then(function(_book) {
book = _book;
});
});
describe('Invalid', function() {
var plugin;
before(function() {
plugin = new Plugin(book, "invalid");
plugin.load("./invalid", PLUGINS_ROOT);
assert(!plugin.isValid());
});
it('should be detected', function() {
should(plugin.isValid()).be.exactly(false);
});
});
describe('empty plugin', function() {
var plugin = new Plugin(books[0], "invalid");
plugin.load("./empty", PLUGINS_ROOT);
describe('Empty', function() {
var plugin;
it('should valid a plugin', function() {
assert(plugin.isValid());
before(function() {
plugin = new Plugin(book, "empty");
plugin.load("./empty", PLUGINS_ROOT);
});
it('should return an empty list of resources', function(done) {
qdone(
plugin.getResources()
.then(function(resources) {
_.each(Plugin.RESOURCES, function(resName) {
assert.equal(resources[resName].length, 0);
it('should valid a plugin', function() {
should(plugin.isValid()).be.exactly(true);
});
it('should return an empty list of resources', function() {
return plugin.getResources()
.then(function(resources) {
_.each(Plugin.RESOURCES, function(resName) {
resources[resName].should.have.lengthOf(0);
});
});
});
});
describe('Resources', function() {
var plugin;
before(function() {
plugin = new Plugin(book, "resources");
plugin.load("./resources", PLUGINS_ROOT);
return book.plugins.load(plugin);
});
it('should valid a plugin', function() {
should(plugin.isValid()).be.exactly(true);
});
describe('Website', function() {
it('should return a valid list of resources', function() {
return plugin.getResources("website")
.then(function(resources) {
resources["js"].should.have.lengthOf(1);
});
}),
done);
});
it('should extend books plugins', function() {
var resources = book.plugins.resources("website");
resources["js"].should.have.lengthOf(1);
});
});
describe('eBook', function() {
it('should return a valid list of resources', function() {
return plugin.getResources("ebook")
.then(function(resources) {
resources["css"].should.have.lengthOf(1);
});
});
it('should extend books plugins', function() {
var resources = book.plugins.resources("ebook");
resources["css"].should.have.lengthOf(1);
});
});
});
describe('resources plugin', function() {
var plugin = new Plugin(books[0], "resources");
plugin.load("./resources", PLUGINS_ROOT);
describe('Filters', function() {
var plugin;
before(function(done) {
qdone(books[0].plugins.load(plugin), done);
before(function() {
plugin = new Plugin(book, "filters");
plugin.load("./filters", PLUGINS_ROOT);
return book.plugins.load(plugin);
});
it('should valid a plugin', function() {
assert(plugin.isValid());
});
it('should return a valid list of resources (website)', function(done) {
qdone(
plugin.getResources("website")
.then(function(resources) {
assert.equal(resources["js"].length, 1);
}),
done);
});
it('should return a valid list of resources (ebook)', function(done) {
qdone(
plugin.getResources("ebook")
.then(function(resources) {
assert.equal(resources["css"].length, 1);
}),
done);
});
it('should extend books plugins (website)', function() {
var resources = books[0].plugins.resources("website");
assert.equal(resources["js"].length, 1);
});
it('should extend books plugins (ebook)', function() {
var resources = books[0].plugins.resources("ebook");
assert.equal(resources["css"].length, 1);
});
});
describe('filters', function() {
var plugin = new Plugin(books[0], "filters");
plugin.load("./filters", PLUGINS_ROOT);
before(function(done) {
qdone(books[0].plugins.load(plugin), done);
});
it('should valid a plugin', function() {
assert(plugin.isValid());
should(plugin.isValid()).be.exactly(true);
});
it('should return a map of filters', function() {
var filters = plugin.getFilters();
assert.equal(_.size(filters), 2);
assert(filters["hello"]);
assert(filters["helloCtx"]);
_.size(filters).should.equal(2);
filters.should.have.property("hello");
filters.should.have.property("helloCtx");
});
it('should correctly extend template filters', function(done) {
qdone(
books[0].template.renderString('{{ "World"|hello }}')
it('should correctly extend template filters', function() {
return book.template.renderString('{{ "World"|hello }}')
.then(function(content) {
assert.equal(content, "Hello World");
}),
done
);
content.should.equal("Hello World");
});
});
it('should correctly set book as context', function(done) {
qdone(
books[0].template.renderString('{{ "root"|helloCtx }}')
it('should correctly set book as context', function() {
return book.template.renderString('{{ "root"|helloCtx }}')
.then(function(content) {
assert.equal(content, "root:"+books[0].root);
}),
done
);
content.should.equal("root:"+book.root);
});
});
});
describe('blocks', function() {
var plugin = new Plugin(books[0], "blocks");
plugin.load("./blocks", PLUGINS_ROOT);
describe('Blocks', function() {
var plugin;
before(function() {
plugin = new Plugin(book, "blocks");
plugin.load("./blocks", PLUGINS_ROOT);
return book.plugins.load(plugin);
});
var testTpl = function(str, args, options) {
return books[0].template.renderString(str, args, options)
.then(books[0].template.postProcess)
return book.template.renderString(str, args, options)
.then(book.template.postProcess)
};
before(function(done) {
qdone(books[0].plugins.load(plugin), done);
});
it('should valid a plugin', function() {
assert(plugin.isValid());
should(plugin.isValid()).be.exactly(true);
});
it('should correctly extend template blocks', function(done) {
qdone(
testTpl('{% test %}hello{% endtest %}')
it('should correctly extend template blocks', function() {
return testTpl('{% test %}hello{% endtest %}')
.then(function(content) {
assert.equal(content, "testhellotest");
}),
done
);
content.should.equal("testhellotest");
});
});
it('should correctly accept shortcuts', function(done) {
qdone(
testTpl('$$hello$$', {}, {
it('should correctly accept shortcuts', function() {
return testTpl('$$hello$$', {}, {
type: "markdown"
})
.then(function(content) {
assert.equal(content, "testhellotest");
}),
done
);
content.should.equal("testhellotest");
});
});
it('should correctly extend template blocks with defined end', function(done) {
qdone(
testTpl('{% test2 %}hello{% endtest2end %}')
it('should correctly extend template blocks with defined end', function() {
return testTpl('{% test2 %}hello{% endtest2end %}')
.then(function(content) {
assert.equal(content, "test2hellotest2");
}),
done
);
content.should.equal("test2hellotest2");
});
});
it('should correctly extend template blocks with sub-blocks', function(done) {
qdone(
testTpl('{% test3join separator=";" %}hello{% also %}world{% endtest3join %}')
it('should correctly extend template blocks with sub-blocks', function() {
return testTpl('{% test3join separator=";" %}hello{% also %}world{% endtest3join %}')
.then(function(content) {
assert.equal(content, "hello;world");
}),
done
);
content.should.equal("hello;world");
});
});
it('should correctly extend template blocks with different sub-blocks', function(done) {
qdone(
testTpl('{% test4join separator=";" %}hello{% also %}the{% finally %}world{% endtest4join %}')
it('should correctly extend template blocks with different sub-blocks', function() {
return testTpl('{% test4join separator=";" %}hello{% also %}the{% finally %}world{% endtest4join %}')
.then(function(content) {
assert.equal(content, "hello;the;world");
}),
done
);
content.should.equal("hello;the;world");
});
});
it('should correctly extend template blocks with arguments', 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");
});
});
});
});
+16 -1
View File
@@ -36,6 +36,21 @@ module.exports = {
assert(blk.blocks[1].name, "finally");
return [blk.body, blk.blocks[0].body, blk.blocks[1].body].join(blk.kwargs.separator);
}
}
},
"test5args": {
process: function(blk) {
return "test5"+blk.args.join(',')+"test5";
}
},
"test5kwargs": {
process: function(blk) {
var s = blk.args.join(',');
for (var key in blk.kwargs) {
s = s + ','+key+':'+blk.kwargs[key];
}
return "test5"+s+"test5";
}
},
}
};
+70
View File
@@ -0,0 +1,70 @@
var fs = require('fs');
var path = require('path');
describe('Summary', function () {
describe('Parsing', function() {
var book;
before(function() {
return books.parse("summary")
.then(function(_book) {
book = _book;
});
});
it('should correctly list items', function() {
book.should.have.property("summary");
book.summary.should.have.property("chapters");
book.summary.chapters.should.have.lengthOf(4);
});
it('should correctly mark non-existant entries', function() {
book.summary.chapters[0].exists.should.have.equal(true);
book.summary.chapters[1].exists.should.have.equal(true);
book.summary.chapters[2].exists.should.have.equal(true);
book.summary.chapters[3].exists.should.have.equal(false);
});
});
describe('Generation', function() {
var book;
before(function() {
return books.generate("summary", "website")
.then(function(_book) {
book = _book;
});
});
it('should create files according to summary', function() {
book.should.have.file("index.html");
book.should.have.file("PAGE1.html");
book.should.have.file("folder/PAGE2.html");
});
it('should correctly output summary', function() {
var PAGE = fs.readFileSync(
path.join(book.options.output, "index.html"),
{ encoding: "utf-8" }
);
PAGE.should.be.html({
".book-summary .chapter[data-level='0'] a": {
attributes: {
href: "./index.html"
}
},
".book-summary .chapter[data-level='1'] a": {
attributes: {
href: "./PAGE1.html"
}
},
".book-summary .chapter[data-level='2'] a": {
attributes: {
href: "./folder/PAGE2.html"
}
}
});
});
});
});
+22 -26
View File
@@ -1,37 +1,33 @@
var path = require('path');
var _ = require('lodash');
var assert = require('assert');
var fs = require("fs");
var pkg = require("../package.json");
describe('Templating', function () {
before(function(done) {
testGeneration(books[0], "website", function(output) {}, done);
var book;
before(function() {
return books.parse("basic")
.then(function(_book) {
book = _book;
});
});
var testTpl = function(str, args, options) {
return books[0].template.renderString(str, args, options)
.then(books[0].template.postProcess)
return book.template.renderString(str, args, options)
.then(book.template.postProcess)
};
it('should correctly have access to generator', function(done) {
qdone(
testTpl('{{ gitbook.generator }}')
.then(function(content) {
assert.equal(content, "website");
}),
done
);
});
describe('Context', function() {
it('should correctly have access to generator', function() {
return testTpl('{{ gitbook.generator }}')
.then(function(content) {
content.should.equal("website");
});
});
it('should correctly have access to gitbook version', function(done) {
qdone(
testTpl('{{ gitbook.version }}')
.then(function(content) {
assert.equal(content, pkg.version.toString());
}),
done
);
it('should correctly have access to gitbook version', function() {
return testTpl('{{ gitbook.version }}')
.then(function(content) {
content.should.equal(pkg.version.toString());
});
});
});
});
+18 -33
View File
@@ -1,40 +1,25 @@
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var assert = require('assert');
var fs = require("fs");
var fsUtil = require("../lib/utils/fs");
describe('Website generator', function () {
describe('Basic Book', function() {
var book;
before(function() {
return books.generate("basic", "website")
.then(function(_book) {
book = _book;
});
});
describe('Website Generator', function () {
it('should correctly generate a book to website', function(done) {
testGeneration(books[1], "website", function(output) {
assert(fs.existsSync(path.join(output, "index.html")));
assert(fs.existsSync(path.join(output, "search_index.json")));
}, done);
});
it('should correctly output an index.html', function() {
book.should.have.file("index.html");
});
it('should correctly include styles in website', function(done) {
testGeneration(books[0], "website", function(output) {
assert(fs.existsSync(path.join(output, "styles/website.css")));
var INDEX = fs.readFileSync(path.join(output, "index.html")).toString();
assert(INDEX.indexOf("styles/website.css") > 0);
}, done);
});
it('should correctly include glossary in website', function(done) {
testGeneration(books[0], "website", function(output) {
assert(fs.existsSync(path.join(output, "GLOSSARY.html")));
}, done);
});
it('should correctly generate a multilingual book to website', function(done) {
testGeneration(books[2], "website", function(output) {
assert(fs.existsSync(path.join(output, "index.html")));
assert(fs.existsSync(path.join(output, "gitbook")));
assert(fs.existsSync(path.join(output, "fr/index.html")));
assert(fs.existsSync(path.join(output, "en/index.html")));
}, done);
it('should correctly copy assets', function() {
book.should.have.file("gitbook");
book.should.have.file("gitbook/app.js");
book.should.have.file("gitbook/style.css");
});
});
});
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More