mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-26 12:18:01 +00:00
Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 38ab9fa05a | |||
| 12e20c5fcc | |||
| 6b103f4532 | |||
| d4fa5c18a8 | |||
| 2a116dfdb0 | |||
| 0349f54f3c | |||
| 695bda312c | |||
| 144bd4c97a | |||
| e105983cf8 | |||
| ea3afbed75 | |||
| d563abe3df | |||
| 70fd4ded29 | |||
| 9ce9388dfc | |||
| c575ad83ae | |||
| 936fa1545b | |||
| c9c2dde2b6 | |||
| d9a1d387c7 | |||
| bf29d187c8 | |||
| d86f9b2667 | |||
| aca4313fc3 | |||
| 4cb5bb06fb | |||
| 24988b75ea | |||
| 775e858d05 | |||
| 1e3d871c1e | |||
| 94a4310c1e | |||
| e8ee57be83 | |||
| 50a132cb2c | |||
| 59be32a54f | |||
| 4ae7cdb5d1 | |||
| 990a4efeb5 | |||
| 9c927dcf13 | |||
| 7a08b3f109 | |||
| 94d4234cb8 | |||
| fbc851bc4e | |||
| e92b661a2a | |||
| b9fe60915f | |||
| 1bf7d092f0 | |||
| 826d472511 | |||
| a6b1f4bd87 | |||
| 004f91f02c | |||
| e6c6ec4293 | |||
| a1d3acf065 | |||
| a732660f59 | |||
| e1bb0638f8 | |||
| 788c0c7e7e | |||
| 762fe16a17 | |||
| 15123805d1 | |||
| 23aecb4f2b | |||
| f1d200b81b | |||
| b566711ee1 | |||
| 22da5aac11 | |||
| f7b95102c5 | |||
| ca70c934d6 | |||
| 1cc48fa314 | |||
| b63b5574d3 | |||
| 67106e8698 | |||
| dc2ab51a88 | |||
| cedfa186f2 | |||
| 80f0d1bfed | |||
| 7178ab1f82 | |||
| bf895b707e | |||
| 996170a4ad | |||
| aa66a4f928 | |||
| 23010fa54a | |||
| 0baebc7979 |
+15
@@ -2,6 +2,21 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 3.2.0
|
||||
- Switch markdown parser from `kramed` to `markup-it`
|
||||
- Fix support of `|` in tables
|
||||
- Fix access to `output.name` in templating
|
||||
- Fix i18n for website and PDF
|
||||
- Fix minor scrolling issues on website
|
||||
- Improve options of default search indexer (keywords, disabling per pages)
|
||||
- Improve pertinence of search results
|
||||
|
||||
## 3.1.1
|
||||
- Fix order of plugins during loading
|
||||
- Fix error when using math and conrefs
|
||||
- Fix target attribute for external links
|
||||
- Fix serve command
|
||||
|
||||
## 3.1.0
|
||||
- Always load themes after plugins
|
||||
- README/Introduction doesn't require to be the first entry in the summary
|
||||
|
||||
@@ -6,10 +6,10 @@ module.exports = {
|
||||
title: 'GitBook Toolchain Documentation',
|
||||
|
||||
// Enforce use of GitBook v3
|
||||
gitbook: '>=3.0.0-pre.0',
|
||||
gitbook: '3.1.1',
|
||||
|
||||
// Use the "official" theme
|
||||
plugins: ['theme-official', 'sitemap'],
|
||||
plugins: ['theme-official@2.1.1', '-sharing', '-fontsettings', 'sitemap'],
|
||||
|
||||
variables: {
|
||||
version: pkg.version
|
||||
|
||||
+2
-8
@@ -20,18 +20,12 @@ GitBook allows you to customize your book using a flexible configuration. These
|
||||
|
||||
Plugins and their configurations are specified in the `book.json`. See [the plugins section](plugins/README.md) for more details.
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `plugins` | List of plugins to load |
|
||||
| `pluginsConfig` |Configuration for plugins |
|
||||
|
||||
### Theme
|
||||
|
||||
Since version 3.0.0, GitBook can use themes. See [the theming section](themes/README.md) for more details.
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `theme` | The theme to use for the book |
|
||||
| `plugins` | List of plugins to load |
|
||||
| `pluginsConfig` |Configuration for plugins |
|
||||
|
||||
### Structure
|
||||
|
||||
|
||||
+4
-3
@@ -29,12 +29,13 @@ You can replace `/usr/bin` with any directory that is in your $PATH.
|
||||
|
||||
### Cover
|
||||
|
||||
Covers are used for all the ebook formats. It's an important part of an ebook brandline.
|
||||
Covers are used for all the ebook formats. You can either provide one yourself, or generate one using the [autocover plugin](https://plugins.gitbook.com/plugin/autocover).
|
||||
|
||||
To provide a cover, place a **`cover.jpg`** file at the root directory of your book. Adding a **`cover_small.jpg`** will specify a smaller version of the cover. The cover should be a **JPEG** file.
|
||||
|
||||
A good cover should respect the following guidelines:
|
||||
|
||||
* Size of 1800x2360 (pixels)
|
||||
* Size of 1800x2360 pixels for `cover.jpg`, 200x262 for `cover_small.jpg`
|
||||
* No border
|
||||
* Clearly visible book title
|
||||
* Any important text should be visible in the small version
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/* Reduce size of logo to allow long versions like 3.0.0-pre.1 */
|
||||
.gb-page-header .container .logo h1 {
|
||||
font-size: 24px ;
|
||||
}
|
||||
Vendored
+16
-1
@@ -17,7 +17,22 @@ A theme is a plugin containing templates and assets. Overriding any individual t
|
||||
|
||||
### Extend/Customize theme in a book
|
||||
|
||||
Authors can extend the templates of a theme directly from their book's source (without creating an external theme). Templates will be resolved in the `_layouts` folder of the book first, then in
|
||||
Authors can extend the templates of a theme directly from their book's source (without creating an external theme). Templates will be resolved in the `_layouts` folder of the book first, then in the installed plugins/themes.
|
||||
|
||||
### Extend instead of Forking
|
||||
|
||||
When you want to make your theme changes available to multiple books, instead of forking the default theme, you can extend it using the [templating syntax](../templating/README.md):
|
||||
|
||||
```html
|
||||
{% extends template.self %}
|
||||
|
||||
{% block body %}
|
||||
{{ super() }}
|
||||
... This will be added to the "body" block
|
||||
{% endblock %}
|
||||
```
|
||||
|
||||
Take a look at the [API](https://github.com/GitbookIO/theme-api) theme for a more complete example.
|
||||
|
||||
### Publish a theme
|
||||
|
||||
|
||||
+11
-4
@@ -6,14 +6,21 @@ module.exports = {
|
||||
// Models
|
||||
Book: require('./models/book'),
|
||||
FS: require('./models/fs'),
|
||||
File: require('./models/file'),
|
||||
Summary: require('./models/summary'),
|
||||
Glossary: require('./models/glossary'),
|
||||
Config: require('./models/config'),
|
||||
Page: require('./models/page'),
|
||||
PluginDependency: require('./models/pluginDependency'),
|
||||
|
||||
// Modifiers
|
||||
SummaryModifier: Modifiers.Summary,
|
||||
ConfigModifier: Modifiers.Config,
|
||||
SummaryModifier: Modifiers.Summary,
|
||||
ConfigModifier: Modifiers.Config,
|
||||
|
||||
// Constants
|
||||
CONFIG_FILES: require('./constants/configFiles.js'),
|
||||
IGNORE_FILES: require('./constants/ignoreFiles.js')
|
||||
CONFIG_FILES: require('./constants/configFiles.js'),
|
||||
IGNORE_FILES: require('./constants/ignoreFiles.js'),
|
||||
DEFAULT_PLUGINS: require('./constants/defaultPlugins'),
|
||||
EXTENSIONS_MARKDOWN: require('./constants/extsMarkdown'),
|
||||
EXTENSIONS_ASCIIDOC: require('./constants/extsAsciidoc')
|
||||
};
|
||||
|
||||
+20
-1
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
var tinylr = require('tiny-lr');
|
||||
var open = require('open');
|
||||
|
||||
var Parse = require('../parse');
|
||||
var Output = require('../output');
|
||||
@@ -32,9 +33,11 @@ function generateBook(args, kwargs) {
|
||||
var outputFolder = getOutputFolder(args);
|
||||
var book = getBook(args, kwargs);
|
||||
var Generator = Output.getGenerator(kwargs.format);
|
||||
var browser = kwargs['browser'];
|
||||
|
||||
var hasWatch = kwargs['watch'];
|
||||
var hasLiveReloading = kwargs['live'];
|
||||
var hasOpen = kwargs['open'];
|
||||
|
||||
// Stop server if running
|
||||
if (server.isRunning()) console.log('Stopping server');
|
||||
@@ -45,7 +48,9 @@ function generateBook(args, kwargs) {
|
||||
.then(function(resultBook) {
|
||||
if (hasLiveReloading) {
|
||||
// Enable livereload plugin
|
||||
resultBook = ConfigModifier.addPlugin(resultBook, 'livereload');
|
||||
var config = resultBook.getConfig();
|
||||
config = ConfigModifier.addPlugin(config, 'livereload');
|
||||
resultBook = resultBook.set('config', config);
|
||||
}
|
||||
|
||||
return Output.generate(Generator, resultBook, {
|
||||
@@ -69,6 +74,10 @@ function generateBook(args, kwargs) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (hasOpen) {
|
||||
open('http://localhost:'+port, browser);
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
if (!hasWatch) {
|
||||
@@ -110,6 +119,16 @@ module.exports = {
|
||||
description: 'Enable live reloading',
|
||||
defaults: true
|
||||
},
|
||||
{
|
||||
name: 'open',
|
||||
description: 'Enable opening book in browser',
|
||||
defaults: false
|
||||
},
|
||||
{
|
||||
name: 'browser',
|
||||
description: 'Specify browser for opening book',
|
||||
defaults: ''
|
||||
},
|
||||
options.log,
|
||||
options.format
|
||||
],
|
||||
|
||||
@@ -18,6 +18,10 @@ module.exports = {
|
||||
'type': 'string',
|
||||
'title': 'ISBN for published book'
|
||||
},
|
||||
'language': {
|
||||
'type': 'string',
|
||||
'title': 'Language of the book'
|
||||
},
|
||||
'author': {
|
||||
'type': 'string',
|
||||
'title': 'Name of the author'
|
||||
|
||||
@@ -1,9 +1,24 @@
|
||||
var Immutable = require('immutable');
|
||||
var PluginDependency = require('../models/pluginDependency');
|
||||
|
||||
var pkg = require('../../package.json');
|
||||
|
||||
/**
|
||||
* Create a PluginDependency from a dependency of gitbook
|
||||
* @param {String} pluginName
|
||||
* @return {PluginDependency}
|
||||
*/
|
||||
function createFromDependency(pluginName) {
|
||||
var npmID = PluginDependency.nameToNpmID(pluginName);
|
||||
var version = pkg.dependencies[npmID];
|
||||
|
||||
return PluginDependency.create(pluginName, version);
|
||||
}
|
||||
|
||||
/*
|
||||
List of default plugins for all books,
|
||||
default plugins should be installed in node dependencies of GitBook
|
||||
*/
|
||||
* List of default plugins for all books,
|
||||
* default plugins should be installed in node dependencies of GitBook
|
||||
*/
|
||||
module.exports = Immutable.List([
|
||||
'highlight',
|
||||
'search',
|
||||
@@ -11,4 +26,4 @@ module.exports = Immutable.List([
|
||||
'sharing',
|
||||
'fontsettings',
|
||||
'theme-default'
|
||||
]);
|
||||
]).map(createFromDependency);
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
module.exports = [
|
||||
'.adoc',
|
||||
'.asciidoc'
|
||||
];
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = [
|
||||
'.md',
|
||||
'.markdown',
|
||||
'.mdown'
|
||||
];
|
||||
@@ -3,12 +3,12 @@ var encodePage = require('./encodePage');
|
||||
var encodeFile = require('./encodeFile');
|
||||
|
||||
/**
|
||||
Return a JSON representation of a book with a specific file
|
||||
|
||||
@param {Book} output
|
||||
@param {Page} page
|
||||
@return {Object}
|
||||
*/
|
||||
* Return a JSON representation of a book with a specific file
|
||||
*
|
||||
* @param {Book} output
|
||||
* @param {Page} page
|
||||
* @return {Object}
|
||||
*/
|
||||
function encodeBookWithPage(book, page) {
|
||||
var file = page.getFile();
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
var encodeBook = require('./encodeBook');
|
||||
|
||||
/**
|
||||
Encode an output to JSON
|
||||
|
||||
@param {Output}
|
||||
@return {Object}
|
||||
*/
|
||||
* Encode an output to JSON
|
||||
*
|
||||
* @param {Output}
|
||||
* @return {Object}
|
||||
*/
|
||||
function encodeOutputToJson(output) {
|
||||
var book = output.getBook();
|
||||
var generator = output.getGenerator();
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
var encodeOutput = require('./encodeOutput');
|
||||
var encodePage = require('./encodePage');
|
||||
var encodeFile = require('./encodeFile');
|
||||
|
||||
/**
|
||||
* Return a JSON representation of a book with a specific file
|
||||
*
|
||||
* @param {Book} output
|
||||
* @param {Page} page
|
||||
* @return {Object}
|
||||
*/
|
||||
function encodeOutputWithPage(output, page) {
|
||||
var file = page.getFile();
|
||||
var book = output.getBook();
|
||||
|
||||
var result = encodeOutput(output);
|
||||
result.page = encodePage(page, book.getSummary());
|
||||
result.file = encodeFile(file);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = encodeOutputWithPage;
|
||||
@@ -2,6 +2,7 @@
|
||||
module.exports = {
|
||||
encodeOutput: require('./encodeOutput'),
|
||||
encodeBookWithPage: require('./encodeBookWithPage'),
|
||||
encodeOutputWithPage: require('./encodeOutputWithPage'),
|
||||
encodeBook: require('./encodeBook'),
|
||||
encodeFile: require('./encodeFile'),
|
||||
encodePage: require('./encodePage'),
|
||||
|
||||
@@ -55,6 +55,36 @@ describe('Config', function() {
|
||||
expect(world).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toReducedVersion', function() {
|
||||
it('must only return diffs for simple values', function() {
|
||||
var _config = Config.createWithValues({
|
||||
gitbook: '3.0.0'
|
||||
});
|
||||
|
||||
var reducedVersion = _config.toReducedVersion();
|
||||
|
||||
expect(reducedVersion.toJS()).toEqual({
|
||||
gitbook: '3.0.0'
|
||||
});
|
||||
});
|
||||
|
||||
it('must only return diffs for deep values', function() {
|
||||
var _config = Config.createWithValues({
|
||||
structure: {
|
||||
readme: 'intro.md'
|
||||
}
|
||||
});
|
||||
|
||||
var reducedVersion = _config.toReducedVersion();
|
||||
|
||||
expect(reducedVersion.toJS()).toEqual({
|
||||
structure: {
|
||||
readme: 'intro.md'
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
var Immutable = require('immutable');
|
||||
var Page = require('../page');
|
||||
|
||||
describe('Page', function() {
|
||||
|
||||
describe('toText', function() {
|
||||
it('must not prepend frontmatter if no attributes', function() {
|
||||
var page = Page().merge({
|
||||
content: 'Hello World'
|
||||
});
|
||||
|
||||
expect(page.toText()).toBe('Hello World');
|
||||
});
|
||||
|
||||
it('must prepend frontmatter if attributes', function() {
|
||||
var page = Page().merge({
|
||||
content: 'Hello World',
|
||||
attributes: Immutable.fromJS({
|
||||
hello: 'world'
|
||||
})
|
||||
});
|
||||
|
||||
expect(page.toText()).toBe('---\nhello: world\n---\n\nHello World');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
var SummaryArticle = require('../summaryArticle');
|
||||
var File = require('../file');
|
||||
|
||||
describe('SummaryArticle', function() {
|
||||
describe('createChildLevel', function() {
|
||||
it('must create the right level', function() {
|
||||
var article = SummaryArticle.create({}, '1.1');
|
||||
expect(article.createChildLevel()).toBe('1.1.1');
|
||||
});
|
||||
|
||||
it('must create the right level when has articles', function() {
|
||||
var article = SummaryArticle.create({
|
||||
articles: [
|
||||
{
|
||||
title: 'Test'
|
||||
}
|
||||
]
|
||||
}, '1.1');
|
||||
expect(article.createChildLevel()).toBe('1.1.2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('isFile', function() {
|
||||
it('must return true when exactly the file', function() {
|
||||
var article = SummaryArticle.create({
|
||||
ref: 'hello.md'
|
||||
}, '1.1');
|
||||
var file = File.createWithFilepath('hello.md');
|
||||
|
||||
expect(article.isFile(file)).toBe(true);
|
||||
});
|
||||
|
||||
it('must return true when path is not normalized', function() {
|
||||
var article = SummaryArticle.create({
|
||||
ref: '/hello.md'
|
||||
}, '1.1');
|
||||
var file = File.createWithFilepath('hello.md');
|
||||
|
||||
expect(article.isFile(file)).toBe(true);
|
||||
});
|
||||
|
||||
it('must return false when has anchor', function() {
|
||||
var article = SummaryArticle.create({
|
||||
ref: 'hello.md#world'
|
||||
}, '1.1');
|
||||
var file = File.createWithFilepath('hello.md');
|
||||
|
||||
expect(article.isFile(file)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
var SummaryPart = require('../summaryPart');
|
||||
|
||||
describe('SummaryPart', function() {
|
||||
describe('createChildLevel', function() {
|
||||
it('must create the right level', function() {
|
||||
var article = SummaryPart.create({}, '1');
|
||||
expect(article.createChildLevel()).toBe('1.1');
|
||||
});
|
||||
|
||||
it('must create the right level when has articles', function() {
|
||||
var article = SummaryPart.create({
|
||||
articles: [
|
||||
{
|
||||
title: 'Test'
|
||||
}
|
||||
]
|
||||
}, '1');
|
||||
expect(article.createChildLevel()).toBe('1.2');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+11
-1
@@ -4,6 +4,7 @@ var Immutable = require('immutable');
|
||||
var File = require('./file');
|
||||
var PluginDependency = require('./pluginDependency');
|
||||
var configDefault = require('../constants/configDefault');
|
||||
var reducedObject = require('../utils/reducedObject');
|
||||
|
||||
var Config = Immutable.Record({
|
||||
file: File(),
|
||||
@@ -18,12 +19,21 @@ Config.prototype.getValues = function() {
|
||||
return this.get('values');
|
||||
};
|
||||
|
||||
/**
|
||||
* Return minimum version of configuration,
|
||||
* Basically it returns the current config minus the default one
|
||||
* @return {Map}
|
||||
*/
|
||||
Config.prototype.toReducedVersion = function() {
|
||||
return reducedObject(configDefault, this.getValues());
|
||||
};
|
||||
|
||||
/**
|
||||
* Render config as text
|
||||
* @return {Promise<String>}
|
||||
*/
|
||||
Config.prototype.toText = function() {
|
||||
return JSON.stringify(this.getValues().toJS(), null, 4);
|
||||
return JSON.stringify(this.toReducedVersion().toJS(), null, 4);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+21
-6
@@ -1,4 +1,5 @@
|
||||
var Immutable = require('immutable');
|
||||
var yaml = require('js-yaml');
|
||||
|
||||
var File = require('./file');
|
||||
|
||||
@@ -32,19 +33,33 @@ Page.prototype.getDir = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
Return path of the page
|
||||
* Return page as text
|
||||
* @return {String}
|
||||
*/
|
||||
Page.prototype.toText = function() {
|
||||
var attrs = this.getAttributes();
|
||||
var content = this.getContent();
|
||||
|
||||
@return {String}
|
||||
if (attrs.size === 0) {
|
||||
return content;
|
||||
}
|
||||
|
||||
var frontMatter = '---\n' + yaml.safeDump(attrs.toJS(), { skipInvalid: true }) + '---\n\n';
|
||||
return (frontMatter + content);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return path of the page
|
||||
* @return {String}
|
||||
*/
|
||||
Page.prototype.getPath = function() {
|
||||
return this.getFile().getPath();
|
||||
};
|
||||
|
||||
/**
|
||||
Create a page for a file
|
||||
|
||||
@param {File} file
|
||||
@return {Page}
|
||||
* Create a page for a file
|
||||
* @param {File} file
|
||||
* @return {Page}
|
||||
*/
|
||||
Page.createForFile = function(file) {
|
||||
return new Page({
|
||||
|
||||
+50
-39
@@ -1,9 +1,10 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var TemplateBlock = require('./templateBlock');
|
||||
var DEFAULT_VERSION = '*';
|
||||
|
||||
var PluginDependency = require('./pluginDependency');
|
||||
var THEME_PREFIX = require('../constants/themePrefix');
|
||||
|
||||
var DEFAULT_VERSION = '*';
|
||||
|
||||
var Plugin = Immutable.Record({
|
||||
name: String(),
|
||||
@@ -17,6 +18,9 @@ var Plugin = Immutable.Record({
|
||||
// Depth of this plugin in the dependency tree
|
||||
depth: Number(0),
|
||||
|
||||
// Parent depending on this plugin
|
||||
parent: String(),
|
||||
|
||||
// Content of the "package.json"
|
||||
package: Immutable.Map(),
|
||||
|
||||
@@ -48,38 +52,48 @@ Plugin.prototype.getDepth = function() {
|
||||
return this.get('depth');
|
||||
};
|
||||
|
||||
/**
|
||||
Return the ID on NPM for this plugin
|
||||
Plugin.prototype.getParent = function() {
|
||||
return this.get('parent');
|
||||
};
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
/**
|
||||
* Return the ID on NPM for this plugin
|
||||
* @return {String}
|
||||
*/
|
||||
Plugin.prototype.getNpmID = function() {
|
||||
return PluginDependency.nameToNpmID(this.getName());
|
||||
};
|
||||
|
||||
/**
|
||||
Check if a plugin is loaded
|
||||
|
||||
@return {Boolean}
|
||||
*/
|
||||
* Check if a plugin is loaded
|
||||
* @return {Boolean}
|
||||
*/
|
||||
Plugin.prototype.isLoaded = function() {
|
||||
return Boolean(this.getPackage().size > 0);
|
||||
};
|
||||
|
||||
/**
|
||||
Return map of hooks
|
||||
@return {Map<String:Function>}
|
||||
*/
|
||||
* Check if a plugin is a theme given its name
|
||||
* @return {Boolean}
|
||||
*/
|
||||
Plugin.prototype.isTheme = function() {
|
||||
var name = this.getName();
|
||||
return (name && name.indexOf(THEME_PREFIX) === 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return map of hooks
|
||||
* @return {Map<String:Function>}
|
||||
*/
|
||||
Plugin.prototype.getHooks = function() {
|
||||
return this.getContent().get('hooks') || Immutable.Map();
|
||||
};
|
||||
|
||||
/**
|
||||
Return infos about resources for a specific type
|
||||
|
||||
@param {String} type
|
||||
@return {Map<String:Mixed>}
|
||||
*/
|
||||
* Return infos about resources for a specific type
|
||||
* @param {String} type
|
||||
* @return {Map<String:Mixed>}
|
||||
*/
|
||||
Plugin.prototype.getResources = function(type) {
|
||||
if (type != 'website' && type != 'ebook') {
|
||||
throw new Error('Invalid assets type ' + type);
|
||||
@@ -92,17 +106,17 @@ Plugin.prototype.getResources = function(type) {
|
||||
};
|
||||
|
||||
/**
|
||||
Return map of filters
|
||||
@return {Map<String:Function>}
|
||||
*/
|
||||
* Return map of filters
|
||||
* @return {Map<String:Function>}
|
||||
*/
|
||||
Plugin.prototype.getFilters = function() {
|
||||
return this.getContent().get('filters');
|
||||
};
|
||||
|
||||
/**
|
||||
Return map of blocks
|
||||
@return {Map<String:TemplateBlock>}
|
||||
*/
|
||||
* Return map of blocks
|
||||
* @return {Map<String:TemplateBlock>}
|
||||
*/
|
||||
Plugin.prototype.getBlocks = function() {
|
||||
var blocks = this.getContent().get('blocks');
|
||||
blocks = blocks || Immutable.Map();
|
||||
@@ -114,21 +128,19 @@ Plugin.prototype.getBlocks = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
Return a specific hook
|
||||
|
||||
@param {String} name
|
||||
@return {Function|undefined}
|
||||
*/
|
||||
* Return a specific hook
|
||||
* @param {String} name
|
||||
* @return {Function|undefined}
|
||||
*/
|
||||
Plugin.prototype.getHook = function(name) {
|
||||
return this.getHooks().get(name);
|
||||
};
|
||||
|
||||
/**
|
||||
Create a plugin from a string
|
||||
|
||||
@param {String}
|
||||
@return {Plugin}
|
||||
*/
|
||||
* Create a plugin from a string
|
||||
* @param {String}
|
||||
* @return {Plugin}
|
||||
*/
|
||||
Plugin.createFromString = function(s) {
|
||||
var parts = s.split('@');
|
||||
var name = parts[0];
|
||||
@@ -141,11 +153,10 @@ Plugin.createFromString = function(s) {
|
||||
};
|
||||
|
||||
/**
|
||||
Create a plugin from a dependency
|
||||
|
||||
@param {PluginDependency}
|
||||
@return {Plugin}
|
||||
*/
|
||||
* Create a plugin from a dependency
|
||||
* @param {PluginDependency}
|
||||
* @return {Plugin}
|
||||
*/
|
||||
Plugin.createFromDep = function(dep) {
|
||||
return new Plugin({
|
||||
name: dep.getName(),
|
||||
|
||||
@@ -30,20 +30,21 @@ SummaryArticle.prototype.getArticles = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
Return how deep the article is.
|
||||
The README has a depth of 1
|
||||
|
||||
@return {Number}
|
||||
*/
|
||||
* Return how deep the article is.
|
||||
* The README has a depth of 1
|
||||
*
|
||||
* @return {Number}
|
||||
*/
|
||||
SummaryArticle.prototype.getDepth = function() {
|
||||
return (this.getLevel().split('.').length - 1);
|
||||
};
|
||||
|
||||
/**
|
||||
Get path (without anchor) to the pointing file
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
* Get path (without anchor) to the pointing file.
|
||||
* It also normalizes the file path.
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
SummaryArticle.prototype.getPath = function() {
|
||||
if (this.isExternal()) {
|
||||
return undefined;
|
||||
@@ -58,24 +59,24 @@ SummaryArticle.prototype.getPath = function() {
|
||||
|
||||
var pathname = (parts.length > 1? parts.slice(0, -1).join('#') : ref);
|
||||
|
||||
// Normalize path to remove ('./', etc)
|
||||
return location.normalize(pathname);
|
||||
// Normalize path to remove ('./', '/...', etc)
|
||||
return location.flatten(pathname);
|
||||
};
|
||||
|
||||
/**
|
||||
Return url if article is external
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
* Return url if article is external
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
SummaryArticle.prototype.getUrl = function() {
|
||||
return this.isExternal()? this.getRef() : undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
Get anchor for this article (or undefined)
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
* Get anchor for this article (or undefined)
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
SummaryArticle.prototype.getAnchor = function() {
|
||||
var ref = this.getRef();
|
||||
var parts = ref.split('#');
|
||||
@@ -85,29 +86,68 @@ SummaryArticle.prototype.getAnchor = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
Is article pointing to a page of an absolute url
|
||||
* Create a new level for a new child article
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
SummaryArticle.prototype.createChildLevel = function() {
|
||||
var level = this.getLevel();
|
||||
var subArticles = this.getArticles();
|
||||
var childLevel = level + '.' + (subArticles.size + 1);
|
||||
|
||||
@return {Boolean}
|
||||
*/
|
||||
return childLevel;
|
||||
};
|
||||
|
||||
/**
|
||||
* Is article pointing to a page of an absolute url
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
SummaryArticle.prototype.isPage = function() {
|
||||
return !this.isExternal() && this.getRef();
|
||||
};
|
||||
|
||||
/**
|
||||
Is article pointing to aan absolute url
|
||||
* Check if this article is a file (exatcly)
|
||||
*
|
||||
* @param {File} file
|
||||
* @return {Boolean}
|
||||
*/
|
||||
SummaryArticle.prototype.isFile = function(file) {
|
||||
return (
|
||||
file.getPath() === this.getPath()
|
||||
&& this.getAnchor() === undefined
|
||||
);
|
||||
};
|
||||
|
||||
@return {Boolean}
|
||||
*/
|
||||
/**
|
||||
* Check if this article is the introduction of the book
|
||||
*
|
||||
* @param {Book|Readme} book
|
||||
* @return {Boolean}
|
||||
*/
|
||||
SummaryArticle.prototype.isReadme = function(book) {
|
||||
var readme = book.getFile? book : book.getReadme();
|
||||
var file = readme.getFile();
|
||||
|
||||
return this.isFile(file);
|
||||
};
|
||||
|
||||
/**
|
||||
* Is article pointing to aan absolute url
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
SummaryArticle.prototype.isExternal = function() {
|
||||
return location.isExternal(this.getRef());
|
||||
};
|
||||
|
||||
/**
|
||||
Create a SummaryArticle
|
||||
|
||||
@param {Object} def
|
||||
@return {SummaryArticle}
|
||||
*/
|
||||
* Create a SummaryArticle
|
||||
*
|
||||
* @param {Object} def
|
||||
* @return {SummaryArticle}
|
||||
*/
|
||||
SummaryArticle.create = function(def, level) {
|
||||
var articles = (def.articles || []).map(function(article, i) {
|
||||
if (article instanceof SummaryArticle) {
|
||||
@@ -124,14 +164,13 @@ SummaryArticle.create = function(def, level) {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Find an article from a base one
|
||||
|
||||
@param {Article|Part} base
|
||||
@param {Function(article)} iter
|
||||
@return {Article}
|
||||
*/
|
||||
* Find an article from a base one
|
||||
*
|
||||
* @param {Article|Part} base
|
||||
* @param {Function(article)} iter
|
||||
* @return {Article}
|
||||
*/
|
||||
SummaryArticle.findArticle = function(base, iter) {
|
||||
var articles = base.getArticles();
|
||||
|
||||
|
||||
@@ -25,11 +25,24 @@ SummaryPart.prototype.getArticles = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
Create a SummaryPart
|
||||
* Create a new level for a new child article
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
SummaryPart.prototype.createChildLevel = function() {
|
||||
var level = this.getLevel();
|
||||
var subArticles = this.getArticles();
|
||||
var childLevel = level + '.' + (subArticles.size + 1);
|
||||
|
||||
@param {Object} def
|
||||
@return {SummaryPart}
|
||||
*/
|
||||
return childLevel;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a SummaryPart
|
||||
*
|
||||
* @param {Object} def
|
||||
* @return {SummaryPart}
|
||||
*/
|
||||
SummaryPart.create = function(def, level) {
|
||||
var articles = (def.articles || []).map(function(article, i) {
|
||||
if (article instanceof SummaryArticle) {
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
var addPlugin = require('../addPlugin');
|
||||
var Config = require('../../../models/config');
|
||||
var Book = require('../../../models/book');
|
||||
|
||||
describe('addPlugin', function() {
|
||||
var config = Config.createWithValues({
|
||||
plugins: ['hello', 'world', '-disabled']
|
||||
});
|
||||
var book = Book().setConfig(config);
|
||||
|
||||
it('should have correct state of dependencies', function() {
|
||||
var disabledDep = config.getPluginDependency('disabled');
|
||||
@@ -17,8 +15,7 @@ describe('addPlugin', function() {
|
||||
});
|
||||
|
||||
it('should add the plugin to the list', function() {
|
||||
var newBook = addPlugin(book, 'test');
|
||||
var newConfig = newBook.getConfig();
|
||||
var newConfig = addPlugin(config, 'test');
|
||||
|
||||
var testDep = newConfig.getPluginDependency('test');
|
||||
expect(testDep).toBeDefined();
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
var removePlugin = require('../removePlugin');
|
||||
var Config = require('../../../models/config');
|
||||
|
||||
describe('removePlugin', function() {
|
||||
var config = Config.createWithValues({
|
||||
plugins: ['hello', 'world', '-disabled']
|
||||
});
|
||||
|
||||
it('should remove the plugin from the list', function() {
|
||||
var newConfig = removePlugin(config, 'hello');
|
||||
|
||||
var testDep = newConfig.getPluginDependency('hello');
|
||||
expect(testDep).toNotBeDefined();
|
||||
});
|
||||
|
||||
it('should remove the disabled plugin from the list', function() {
|
||||
var newConfig = removePlugin(config, 'disabled');
|
||||
|
||||
var testDep = newConfig.getPluginDependency('disabled');
|
||||
expect(testDep).toNotBeDefined();
|
||||
});
|
||||
|
||||
it('should disable default plugin', function() {
|
||||
var newConfig = removePlugin(config, 'search');
|
||||
|
||||
var disabledDep = newConfig.getPluginDependency('search');
|
||||
expect(disabledDep).toBeDefined();
|
||||
expect(disabledDep.getVersion()).toEqual('*');
|
||||
expect(disabledDep.isEnabled()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
var togglePlugin = require('../togglePlugin');
|
||||
var Config = require('../../../models/config');
|
||||
var Book = require('../../../models/book');
|
||||
|
||||
describe('togglePlugin', function() {
|
||||
var config = Config.createWithValues({
|
||||
plugins: ['hello', 'world', '-disabled']
|
||||
});
|
||||
var book = Book().setConfig(config);
|
||||
|
||||
it('should enable plugin', function() {
|
||||
var newBook = togglePlugin(book, 'disabled');
|
||||
var newConfig = newBook.getConfig();
|
||||
var newConfig = togglePlugin(config, 'disabled');
|
||||
|
||||
var testDep = newConfig.getPluginDependency('disabled');
|
||||
expect(testDep).toBeDefined();
|
||||
@@ -19,8 +16,7 @@ describe('togglePlugin', function() {
|
||||
});
|
||||
|
||||
it('should disable plugin', function() {
|
||||
var newBook = togglePlugin(book, 'world');
|
||||
var newConfig = newBook.getConfig();
|
||||
var newConfig = togglePlugin(config, 'world');
|
||||
|
||||
var testDep = newConfig.getPluginDependency('world');
|
||||
expect(testDep).toBeDefined();
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
var PluginDependency = require('../../models/pluginDependency');
|
||||
var togglePlugin = require('./togglePlugin');
|
||||
var isDefaultPlugin = require('./isDefaultPlugin');
|
||||
|
||||
/**
|
||||
* Add a plugin to a book's configuration
|
||||
* @param {Book} book
|
||||
* @param {String} plugin
|
||||
* @param {Config} config
|
||||
* @param {String} pluginName
|
||||
* @param {String} version (optional)
|
||||
* @return {Book}
|
||||
* @return {Config}
|
||||
*/
|
||||
function addPlugin(book, plugin, version) {
|
||||
var config = book.getConfig();
|
||||
var deps = config.getPluginDependencies();
|
||||
function addPlugin(config, pluginName, version) {
|
||||
// For default plugin, we only ensure it is enabled
|
||||
if (isDefaultPlugin(pluginName, version)) {
|
||||
return togglePlugin(config, pluginName, true);
|
||||
}
|
||||
|
||||
var dep = PluginDependency.create(plugin, version);
|
||||
var deps = config.getPluginDependencies();
|
||||
var dep = PluginDependency.create(pluginName, version);
|
||||
|
||||
deps = deps.push(dep);
|
||||
config = config.setPluginDependencies(deps);
|
||||
|
||||
return book.setConfig(config);
|
||||
return config.setPluginDependencies(deps);
|
||||
}
|
||||
|
||||
module.exports = addPlugin;
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
|
||||
/**
|
||||
* Edit ocnfiguration of a plugin
|
||||
* @param {Book} book
|
||||
* Edit configuration of a plugin
|
||||
* @param {Config} config
|
||||
* @param {String} plugin
|
||||
* @param {Object} pluginConfig
|
||||
* @return {Book}
|
||||
* @return {Config}
|
||||
*/
|
||||
function editPlugin(book, pluginName, pluginConfig) {
|
||||
var config = book.getConfig();
|
||||
config = config.set('pluginsConfig.'+pluginName, pluginConfig);
|
||||
|
||||
return book.setConfig(config);
|
||||
function editPlugin(config, pluginName, pluginConfig) {
|
||||
return config.setValue('pluginsConfig.'+pluginName, pluginConfig);
|
||||
}
|
||||
|
||||
module.exports = editPlugin;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Return the configuration for a plugin
|
||||
* @param {Config} config
|
||||
* @param {String} pluginName
|
||||
* @return {Object}
|
||||
*/
|
||||
function getPluginConfig(config, pluginName) {
|
||||
var pluginsConfig = config.getValues().get('pluginsConfig');
|
||||
if (pluginsConfig === undefined) {
|
||||
return {};
|
||||
}
|
||||
var pluginConf = pluginsConfig.get(pluginName);
|
||||
if (pluginConf === undefined) {
|
||||
return {};
|
||||
} else {
|
||||
return pluginConf.toJS();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = getPluginConfig;
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
/**
|
||||
* Test if a plugin is listed
|
||||
* @param { {List<PluginDependency}} deps
|
||||
* @param {String} plugin
|
||||
* @param {String} version
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function hasPlugin(deps, pluginName, version) {
|
||||
return !!deps.find(function(dep) {
|
||||
return dep.getName() === pluginName && (!version || dep.getVersion() === version);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = hasPlugin;
|
||||
@@ -1,7 +1,10 @@
|
||||
|
||||
module.exports = {
|
||||
addPlugin: require('./addPlugin'),
|
||||
removePlugin: require('./removePlugin'),
|
||||
togglePlugin: require('./togglePlugin'),
|
||||
editPlugin: require('./editPlugin')
|
||||
addPlugin: require('./addPlugin'),
|
||||
removePlugin: require('./removePlugin'),
|
||||
togglePlugin: require('./togglePlugin'),
|
||||
editPlugin: require('./editPlugin'),
|
||||
hasPlugin: require('./hasPlugin'),
|
||||
getPluginConfig: require('./getPluginConfig'),
|
||||
isDefaultPlugin: require('./isDefaultPlugin')
|
||||
};
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
var DEFAULT_PLUGINS = require('../../constants/defaultPlugins');
|
||||
var hasPlugin = require('./hasPlugin');
|
||||
|
||||
/**
|
||||
* Test if a plugin is a default one
|
||||
* @param {String} plugin
|
||||
* @param {String} version
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isDefaultPlugin(pluginName, version) {
|
||||
return hasPlugin(DEFAULT_PLUGINS, pluginName, version);
|
||||
}
|
||||
|
||||
module.exports = isDefaultPlugin;
|
||||
@@ -1,21 +1,25 @@
|
||||
var togglePlugin = require('./togglePlugin');
|
||||
var isDefaultPlugin = require('./isDefaultPlugin');
|
||||
|
||||
/**
|
||||
* Remove a plugin from a book's configuration
|
||||
* @param {Book} book
|
||||
* @param {Config} config
|
||||
* @param {String} plugin
|
||||
* @return {Book}
|
||||
* @return {Config}
|
||||
*/
|
||||
function removePlugin(book, pluginName) {
|
||||
var config = book.getConfig();
|
||||
function removePlugin(config, pluginName) {
|
||||
var deps = config.getPluginDependencies();
|
||||
|
||||
// For default plugin, we have to disable it instead of removing from the list
|
||||
if (isDefaultPlugin(pluginName)) {
|
||||
return togglePlugin(config, pluginName, false);
|
||||
}
|
||||
|
||||
deps = deps.filter(function(dep) {
|
||||
// Remove the dependency from the list
|
||||
deps = deps.filterNot(function(dep) {
|
||||
return dep.getName() === pluginName;
|
||||
});
|
||||
config = config.setPluginDependencies(deps);
|
||||
|
||||
return book.setConfig(config);
|
||||
return config.setPluginDependencies(deps);
|
||||
}
|
||||
|
||||
module.exports = removePlugin;
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
var PluginDependency = require('../../models/pluginDependency');
|
||||
var hasPlugin = require('./hasPlugin');
|
||||
var isDefaultPlugin = require('./isDefaultPlugin');
|
||||
|
||||
/**
|
||||
* Enable/disable a plugin dependency
|
||||
* @param {Book} book
|
||||
* @param {String} plugin
|
||||
* @param {Config} config
|
||||
* @param {String} pluginName
|
||||
* @param {Boolean} state (optional)
|
||||
* @return {Book}
|
||||
* @return {Config}
|
||||
*/
|
||||
function togglePlugin(book, plugin, state) {
|
||||
var config = book.getConfig();
|
||||
function togglePlugin(config, pluginName, state) {
|
||||
var deps = config.getPluginDependencies();
|
||||
|
||||
// For default plugin, we should ensure it's listed first
|
||||
if (isDefaultPlugin(pluginName) && !hasPlugin(deps, pluginName)) {
|
||||
deps = deps.push(PluginDependency.create(pluginName));
|
||||
}
|
||||
|
||||
deps = deps.map(function(dep) {
|
||||
if (dep.getName() === plugin) {
|
||||
if (dep.getName() === pluginName) {
|
||||
return dep.toggle(state);
|
||||
}
|
||||
|
||||
return dep;
|
||||
});
|
||||
|
||||
config = config.setPluginDependencies(deps);
|
||||
return book.setConfig(config);
|
||||
return config.setPluginDependencies(deps);
|
||||
}
|
||||
|
||||
module.exports = togglePlugin;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
var Summary = require('../../../models/summary');
|
||||
var SummaryPart = require('../../../models/summaryPart');
|
||||
|
||||
var File = require('../../../models/file');
|
||||
|
||||
describe('insertPart', function() {
|
||||
var insertPart = require('../insertPart');
|
||||
var summary = Summary.createFromParts(File(), [
|
||||
{
|
||||
articles: [
|
||||
{
|
||||
title: '1.1',
|
||||
path: '1.1'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Part I',
|
||||
articles: [
|
||||
{
|
||||
title: '2.1',
|
||||
path: '2.1',
|
||||
articles: []
|
||||
},
|
||||
{
|
||||
title: '2.2',
|
||||
path: '2.2'
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
it('should insert an part at a given level', function() {
|
||||
var part = SummaryPart.create({
|
||||
title: 'Inserted'
|
||||
}, 'meaningless.level');
|
||||
|
||||
var newSummary = insertPart(summary, part, 1);
|
||||
|
||||
var inserted = newSummary.getPart(1);
|
||||
expect(inserted.getTitle()).toBe('Inserted');
|
||||
expect(newSummary.getParts().count()).toBe(3);
|
||||
|
||||
var otherArticle = newSummary.getByLevel('3.1');
|
||||
expect(otherArticle.getTitle()).toBe('2.1');
|
||||
expect(otherArticle.getLevel()).toBe('3.1');
|
||||
});
|
||||
|
||||
it('should insert an part in last position', function() {
|
||||
var part = SummaryPart.create({
|
||||
title: 'Inserted'
|
||||
}, 'meaningless.level');
|
||||
|
||||
var newSummary = insertPart(summary, part, 2);
|
||||
|
||||
var inserted = newSummary.getPart(2);
|
||||
expect(inserted.getTitle()).toBe('Inserted');
|
||||
expect(newSummary.getParts().count()).toBe(3);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var Output = require('../../models/output');
|
||||
var Book = require('../../models/book');
|
||||
var parseBook = require('../../parse/parseBook');
|
||||
var createMockFS = require('../../fs/mock');
|
||||
var preparePlugins = require('../preparePlugins');
|
||||
|
||||
/**
|
||||
* Create an output using a generator
|
||||
*
|
||||
* FOR TESTING PURPOSE ONLY
|
||||
*
|
||||
* @param {Generator} generator
|
||||
* @param {Map<String:String|Map>} files
|
||||
* @return {Promise<Output>}
|
||||
*/
|
||||
function createMockOutput(generator, files, options) {
|
||||
var fs = createMockFS(files);
|
||||
var book = Book.createForFS(fs);
|
||||
var state = generator.State? generator.State({}) : Immutable.Map();
|
||||
|
||||
book = book.setLogLevel('disabled');
|
||||
options = generator.Options(options);
|
||||
|
||||
return parseBook(book)
|
||||
.then(function(resultBook) {
|
||||
return new Output({
|
||||
book: resultBook,
|
||||
options: options,
|
||||
state: state,
|
||||
generator: generator.name
|
||||
});
|
||||
})
|
||||
.then(preparePlugins);
|
||||
}
|
||||
|
||||
module.exports = createMockOutput;
|
||||
@@ -1,4 +1,4 @@
|
||||
var generateMock = require('../generateMock');
|
||||
var generateMock = require('./generateMock');
|
||||
var EbookGenerator = require('../ebook');
|
||||
|
||||
describe('EbookGenerator', function() {
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
var tmp = require('tmp');
|
||||
|
||||
var Book = require('../models/book');
|
||||
var createMockFS = require('../fs/mock');
|
||||
var parseBook = require('../parse/parseBook');
|
||||
var generateBook = require('./generateBook');
|
||||
|
||||
var Book = require('../../models/book');
|
||||
var createMockFS = require('../../fs/mock');
|
||||
var parseBook = require('../../parse/parseBook');
|
||||
var generateBook = require('../generateBook');
|
||||
|
||||
/**
|
||||
Generate a book using JSON generator
|
||||
And returns the path to the output dir.
|
||||
|
||||
FOR TESTING PURPOSE ONLY
|
||||
|
||||
@param {Generator}
|
||||
@param {Map<String:String|Map>} files
|
||||
@return {Promise<String>}
|
||||
*/
|
||||
* Generate a book using a generator
|
||||
* And returns the path to the output dir.
|
||||
*
|
||||
* FOR TESTING PURPOSE ONLY
|
||||
*
|
||||
* @param {Generator}
|
||||
* @param {Map<String:String|Map>} files
|
||||
* @return {Promise<String>}
|
||||
*/
|
||||
function generateMock(Generator, files) {
|
||||
var fs = createMockFS(files);
|
||||
var book = Book.createForFS(fs);
|
||||
@@ -1,4 +1,4 @@
|
||||
var generateMock = require('../generateMock');
|
||||
var generateMock = require('./generateMock');
|
||||
var JSONGenerator = require('../json');
|
||||
|
||||
describe('JSONGenerator', function() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var fs = require('fs');
|
||||
var generateMock = require('../generateMock');
|
||||
var generateMock = require('./generateMock');
|
||||
var WebsiteGenerator = require('../website');
|
||||
|
||||
describe('WebsiteGenerator', function() {
|
||||
|
||||
+26
-28
@@ -13,13 +13,12 @@ var generateAssets = require('./generateAssets');
|
||||
var generatePages = require('./generatePages');
|
||||
|
||||
/**
|
||||
Process an output to generate the book
|
||||
|
||||
@param {Generator} generator
|
||||
@param {Output} output
|
||||
|
||||
@return {Promise<Output>}
|
||||
*/
|
||||
* Process an output to generate the book
|
||||
*
|
||||
* @param {Generator} generator
|
||||
* @param {Output} output
|
||||
* @return {Promise<Output>}
|
||||
*/
|
||||
function processOutput(generator, startOutput) {
|
||||
return Promise(startOutput)
|
||||
.then(preparePlugins)
|
||||
@@ -133,27 +132,26 @@ function processOutput(generator, startOutput) {
|
||||
}
|
||||
|
||||
/**
|
||||
Generate a book using a generator.
|
||||
|
||||
The overall process is:
|
||||
1. List and load plugins for this book
|
||||
2. Call hook "config"
|
||||
3. Call hook "init"
|
||||
4. Initialize generator
|
||||
5. List all assets and pages
|
||||
6. Copy all assets to output
|
||||
7. Generate all pages
|
||||
8. Call hook "finish:before"
|
||||
9. Finish generation
|
||||
10. Call hook "finish"
|
||||
|
||||
|
||||
@param {Generator} generator
|
||||
@param {Book} book
|
||||
@param {Object} options
|
||||
|
||||
@return {Promise<Output>}
|
||||
*/
|
||||
* Generate a book using a generator.
|
||||
*
|
||||
* The overall process is:
|
||||
* 1. List and load plugins for this book
|
||||
* 2. Call hook "config"
|
||||
* 3. Call hook "init"
|
||||
* 4. Initialize generator
|
||||
* 5. List all assets and pages
|
||||
* 6. Copy all assets to output
|
||||
* 7. Generate all pages
|
||||
* 8. Call hook "finish:before"
|
||||
* 9. Finish generation
|
||||
* 10. Call hook "finish"
|
||||
*
|
||||
*
|
||||
* @param {Generator} generator
|
||||
* @param {Book} book
|
||||
* @param {Object} options
|
||||
* @return {Promise<Output>}
|
||||
*/
|
||||
function generateBook(generator, book, options) {
|
||||
options = generator.Options(options);
|
||||
var state = generator.State? generator.State({}) : Immutable.Map();
|
||||
|
||||
@@ -10,12 +10,12 @@ var createTemplateEngine = require('./createTemplateEngine');
|
||||
var callPageHook = require('./callPageHook');
|
||||
|
||||
/**
|
||||
Prepare and generate HTML for a page
|
||||
|
||||
@param {Output} output
|
||||
@param {Page} page
|
||||
@return {Promise<Page>}
|
||||
*/
|
||||
* Prepare and generate HTML for a page
|
||||
*
|
||||
* @param {Output} output
|
||||
* @param {Page} page
|
||||
* @return {Promise<Page>}
|
||||
*/
|
||||
function generatePage(output, page) {
|
||||
var book = output.getBook();
|
||||
var engine = createTemplateEngine(output);
|
||||
@@ -27,7 +27,7 @@ function generatePage(output, page) {
|
||||
var file = resultPage.getFile();
|
||||
var filePath = file.getPath();
|
||||
var parser = file.getParser();
|
||||
var context = JSONUtils.encodeBookWithPage(book, resultPage);
|
||||
var context = JSONUtils.encodeOutputWithPage(output, resultPage);
|
||||
|
||||
if (!parser) {
|
||||
return Promise.reject(error.FileNotParsableError({
|
||||
|
||||
@@ -9,11 +9,11 @@ var fileToOutput = require('./helper/fileToOutput');
|
||||
var CODEBLOCK = 'code';
|
||||
|
||||
/**
|
||||
Return default modifier to prepare a page for
|
||||
rendering.
|
||||
|
||||
@return {Array<Modifier>}
|
||||
*/
|
||||
* Return default modifier to prepare a page for
|
||||
* rendering.
|
||||
*
|
||||
* @return {Array<Modifier>}
|
||||
*/
|
||||
function getModifiers(output, page) {
|
||||
var book = output.getBook();
|
||||
var plugins = output.getPlugins();
|
||||
|
||||
@@ -6,12 +6,12 @@ var LocationUtils = require('../../utils/location');
|
||||
var OUTPUT_EXTENSION = '.html';
|
||||
|
||||
/**
|
||||
Convert a filePath (absolute) to a filename for output
|
||||
|
||||
@param {Output} output
|
||||
@param {String} filePath
|
||||
@return {String}
|
||||
*/
|
||||
* Convert a filePath (absolute) to a filename for output
|
||||
*
|
||||
* @param {Output} output
|
||||
* @param {String} filePath
|
||||
* @return {String}
|
||||
*/
|
||||
function fileToOutput(output, filePath) {
|
||||
var book = output.getBook();
|
||||
var readme = book.getReadme();
|
||||
|
||||
@@ -3,12 +3,12 @@ var LocationUtils = require('../../utils/location');
|
||||
var fileToURL = require('./fileToURL');
|
||||
|
||||
/**
|
||||
Resolve an absolute path (extracted from a link)
|
||||
|
||||
@param {Output} output
|
||||
@param {String} filePath
|
||||
@return {String}
|
||||
*/
|
||||
* Resolve an absolute path (extracted from a link)
|
||||
*
|
||||
* @param {Output} output
|
||||
* @param {String} filePath
|
||||
* @return {String}
|
||||
*/
|
||||
function resolveFileToURL(output, filePath) {
|
||||
// Convert /test.png -> test.png
|
||||
filePath = LocationUtils.toAbsolute(filePath, '', '');
|
||||
|
||||
@@ -7,11 +7,11 @@ var getModifiers = require('../getModifiers');
|
||||
var JSON_VERSION = '3';
|
||||
|
||||
/**
|
||||
Write a page as a json file
|
||||
|
||||
@param {Output} output
|
||||
@param {Page} page
|
||||
*/
|
||||
* Write a page as a json file
|
||||
*
|
||||
* @param {Output} output
|
||||
* @param {Page} page
|
||||
*/
|
||||
function onPage(output, page) {
|
||||
var file = page.getFile();
|
||||
var readme = output.getBook().getReadme().getFile();
|
||||
|
||||
@@ -87,6 +87,18 @@ describe('resolveLinks', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('External link', function() {
|
||||
var TEST = '<p>This is a <a href="http://www.github.com">external link</a></p>';
|
||||
|
||||
it('should have target="_blank" attribute', function() {
|
||||
var $ = cheerio.load(TEST);
|
||||
|
||||
return resolveLinks('hello.md', resolveFileBasic, $)
|
||||
.then(function() {
|
||||
var link = $('a');
|
||||
expect(link.attr('target')).toBe('_blank');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -59,19 +59,18 @@ function replaceText($, el, search, replace, text_only ) {
|
||||
}
|
||||
|
||||
/**
|
||||
Annotate text using a list of GlossaryEntry
|
||||
|
||||
@param {List<GlossaryEntry>}
|
||||
@param {String} glossaryFilePath
|
||||
@param {HTMLDom} $
|
||||
*/
|
||||
* Annotate text using a list of GlossaryEntry
|
||||
*
|
||||
* @param {List<GlossaryEntry>}
|
||||
* @param {String} glossaryFilePath
|
||||
* @param {HTMLDom} $
|
||||
*/
|
||||
function annotateText(entries, glossaryFilePath, $) {
|
||||
entries.forEach(function(entry) {
|
||||
var entryId = entry.getID();
|
||||
var name = entry.getName();
|
||||
var entryId = entry.getID();
|
||||
var name = entry.getName();
|
||||
var description = entry.getDescription();
|
||||
|
||||
var searchRegex = new RegExp( '\\b(' + pregQuote(name.toLowerCase()) + ')\\b' , 'gi' );
|
||||
var searchRegex = new RegExp( '\\b(' + pregQuote(name.toLowerCase()) + ')\\b' , 'gi' );
|
||||
|
||||
$('*').each(function() {
|
||||
var $this = $(this);
|
||||
|
||||
@@ -24,7 +24,7 @@ function resolveLinks(currentFile, resolveFile, $) {
|
||||
}
|
||||
|
||||
if (LocationUtils.isExternal(href)) {
|
||||
$a.attr('_target', 'blank');
|
||||
$a.attr('target', '_blank');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@ var Plugins = require('../plugins');
|
||||
var Promise = require('../utils/promise');
|
||||
|
||||
/**
|
||||
Load and setup plugins
|
||||
|
||||
@param {Output}
|
||||
@return {Promise<Output>}
|
||||
*/
|
||||
* Load and setup plugins
|
||||
*
|
||||
* @param {Output}
|
||||
* @return {Promise<Output>}
|
||||
*/
|
||||
function preparePlugins(output) {
|
||||
var book = output.getBook();
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
var createMockOutput = require('../../__tests__/createMock');
|
||||
var prepareI18n = require('../prepareI18n');
|
||||
var createTemplateEngine = require('../createTemplateEngine');
|
||||
|
||||
var WebsiteGenerator = require('../');
|
||||
|
||||
describe('i18n', function() {
|
||||
it('should correctly use english as default language', function() {
|
||||
return createMockOutput(WebsiteGenerator, {
|
||||
'README.md': 'Hello World'
|
||||
})
|
||||
.then(function(output) {
|
||||
return prepareI18n(output);
|
||||
})
|
||||
.then(function(output) {
|
||||
var engine = createTemplateEngine(output, 'README.md');
|
||||
var t = engine.getFilters().get('t');
|
||||
|
||||
expect(t('SUMMARY_INTRODUCTION')).toEqual('Introduction');
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly use language from book.json', function() {
|
||||
return createMockOutput(WebsiteGenerator, {
|
||||
'README.md': 'Hello World',
|
||||
'book.json': JSON.stringify({ language: 'fr' })
|
||||
})
|
||||
.then(function(output) {
|
||||
return prepareI18n(output);
|
||||
})
|
||||
.then(function(output) {
|
||||
var engine = createTemplateEngine(output, 'README.md');
|
||||
var t = engine.getFilters().get('t');
|
||||
|
||||
expect(t('GITBOOK_LINK')).toEqual('Publié avec GitBook');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -18,19 +18,19 @@ var fileToURL = require('../helper/fileToURL');
|
||||
var resolveFileToURL = require('../helper/resolveFileToURL');
|
||||
|
||||
/**
|
||||
Directory for a theme with the templates
|
||||
*/
|
||||
* Directory for a theme with the templates
|
||||
*/
|
||||
function templateFolder(dir) {
|
||||
return path.join(dir, templatesFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
Create templating engine to render themes
|
||||
|
||||
@param {Output} output
|
||||
@param {String} currentFile
|
||||
@return {TemplateEngine}
|
||||
*/
|
||||
* Create templating engine to render themes
|
||||
*
|
||||
* @param {Output} output
|
||||
* @param {String} currentFile
|
||||
* @return {TemplateEngine}
|
||||
*/
|
||||
function createTemplateEngine(output, currentFile) {
|
||||
var book = output.getBook();
|
||||
var state = output.getState();
|
||||
@@ -47,18 +47,17 @@ function createTemplateEngine(output, currentFile) {
|
||||
var loader = new Templating.ThemesLoader(tplSearchPaths);
|
||||
|
||||
// Get languages
|
||||
var language = config.get('language');
|
||||
var language = config.getValue('language');
|
||||
|
||||
// Create API context
|
||||
var context = Api.encodeGlobal(output);
|
||||
|
||||
|
||||
/**
|
||||
Check if a file exists
|
||||
|
||||
@param {String} fileName
|
||||
@return {Boolean}
|
||||
*/
|
||||
* Check if a file exists
|
||||
* @param {String} fileName
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function fileExists(fileName) {
|
||||
if (!fileName) {
|
||||
return false;
|
||||
@@ -69,11 +68,10 @@ function createTemplateEngine(output, currentFile) {
|
||||
}
|
||||
|
||||
/**
|
||||
Return an article by its path
|
||||
|
||||
@param {String} filePath
|
||||
@return {Object|undefined}
|
||||
*/
|
||||
* Return an article by its path
|
||||
* @param {String} filePath
|
||||
* @return {Object|undefined}
|
||||
*/
|
||||
function getArticleByPath(filePath) {
|
||||
var article = summary.getByPath(filePath);
|
||||
if (!article) return undefined;
|
||||
@@ -82,11 +80,10 @@ function createTemplateEngine(output, currentFile) {
|
||||
}
|
||||
|
||||
/**
|
||||
Return a page by its path
|
||||
|
||||
@param {String} filePath
|
||||
@return {Object|undefined}
|
||||
*/
|
||||
* Return a page by its path
|
||||
* @param {String} filePath
|
||||
* @return {Object|undefined}
|
||||
*/
|
||||
function getPageByPath(filePath) {
|
||||
var page = output.getPage(filePath);
|
||||
if (!page) return undefined;
|
||||
@@ -94,7 +91,6 @@ function createTemplateEngine(output, currentFile) {
|
||||
return JSONUtils.encodePage(page, summary);
|
||||
}
|
||||
|
||||
|
||||
return TemplateEngine.create({
|
||||
loader: loader,
|
||||
|
||||
@@ -108,17 +104,17 @@ function createTemplateEngine(output, currentFile) {
|
||||
|
||||
filters: defaultFilters.merge({
|
||||
/**
|
||||
Translate a sentence
|
||||
*/
|
||||
* Translate a sentence
|
||||
*/
|
||||
t: function t(s) {
|
||||
return i18n.t(language, s);
|
||||
},
|
||||
|
||||
/**
|
||||
Resolve an absolute file path into a
|
||||
relative path.
|
||||
it also resolve pages
|
||||
*/
|
||||
* Resolve an absolute file path into a
|
||||
* relative path.
|
||||
* it also resolve pages
|
||||
*/
|
||||
resolveFile: function(filePath) {
|
||||
filePath = resolveFileToURL(output, filePath);
|
||||
return LocationUtils.relativeForFile(currentFile, filePath);
|
||||
|
||||
@@ -12,11 +12,11 @@ var createTemplateEngine = require('./createTemplateEngine');
|
||||
var fileToOutput = require('../helper/fileToOutput');
|
||||
|
||||
/**
|
||||
Write a page as a json file
|
||||
|
||||
@param {Output} output
|
||||
@param {Page} page
|
||||
*/
|
||||
* Write a page as a json file
|
||||
*
|
||||
* @param {Output} output
|
||||
* @param {Page} page
|
||||
*/
|
||||
function onPage(output, page) {
|
||||
var options = output.getOptions();
|
||||
var prefix = options.get('prefix');
|
||||
@@ -40,7 +40,7 @@ function onPage(output, page) {
|
||||
return Modifiers.modifyHTML(page, getModifiers(output, page))
|
||||
.then(function(resultPage) {
|
||||
// Generate the context
|
||||
var context = JSONUtils.encodeBookWithPage(output.getBook(), resultPage);
|
||||
var context = JSONUtils.encodeOutputWithPage(output, resultPage);
|
||||
context.plugins = {
|
||||
resources: Plugins.listResources(plugins, resources).toJS()
|
||||
};
|
||||
|
||||
@@ -5,11 +5,11 @@ var Promise = require('../../utils/promise');
|
||||
var listSearchPaths = require('./listSearchPaths');
|
||||
|
||||
/**
|
||||
Prepare i18n, load translations from plugins and book
|
||||
|
||||
@param {Output}
|
||||
@return {Promise<Output>}
|
||||
*/
|
||||
* Prepare i18n, load translations from plugins and book
|
||||
*
|
||||
* @param {Output}
|
||||
* @return {Promise<Output>}
|
||||
*/
|
||||
function prepareI18n(output) {
|
||||
var state = output.getState();
|
||||
var i18n = state.getI18n();
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
var parsePageFromString = require('../parsePageFromString');
|
||||
var Page = require('../../models/page');
|
||||
|
||||
describe('parsePageFromString', function() {
|
||||
var page = new Page();
|
||||
|
||||
it('should parse YAML frontmatter', function() {
|
||||
var CONTENT = '---\nhello: true\nworld: "cool"\n---\n# Hello World\n';
|
||||
var newPage = parsePageFromString(page, CONTENT);
|
||||
|
||||
expect(newPage.getDir()).toBe('ltr');
|
||||
expect(newPage.getContent()).toBe('# Hello World\n');
|
||||
|
||||
var attrs = newPage.getAttributes();
|
||||
expect(attrs.size).toBe(2);
|
||||
expect(attrs.get('hello')).toBe(true);
|
||||
expect(attrs.get('world')).toBe('cool');
|
||||
});
|
||||
|
||||
it('should parse text direction (english)', function() {
|
||||
var CONTENT = 'Hello World';
|
||||
var newPage = parsePageFromString(page, CONTENT);
|
||||
|
||||
expect(newPage.getDir()).toBe('ltr');
|
||||
expect(newPage.getContent()).toBe('Hello World');
|
||||
expect(newPage.getAttributes().size).toBe(0);
|
||||
});
|
||||
|
||||
it('should parse text direction (arab)', function() {
|
||||
var CONTENT = 'مرحبا بالعالم';
|
||||
var newPage = parsePageFromString(page, CONTENT);
|
||||
|
||||
expect(newPage.getDir()).toBe('rtl');
|
||||
expect(newPage.getContent()).toBe('مرحبا بالعالم');
|
||||
expect(newPage.getAttributes().size).toBe(0);
|
||||
});
|
||||
});
|
||||
+12
-11
@@ -1,14 +1,15 @@
|
||||
|
||||
module.exports = {
|
||||
parseBook: require('./parseBook'),
|
||||
parseSummary: require('./parseSummary'),
|
||||
parseGlossary: require('./parseGlossary'),
|
||||
parseReadme: require('./parseReadme'),
|
||||
parseConfig: require('./parseConfig'),
|
||||
parsePagesList: require('./parsePagesList'),
|
||||
parseIgnore: require('./parseIgnore'),
|
||||
listAssets: require('./listAssets'),
|
||||
parseLanguages: require('./parseLanguages'),
|
||||
parsePage: require('./parsePage'),
|
||||
lookupStructureFile: require('./lookupStructureFile')
|
||||
parseBook: require('./parseBook'),
|
||||
parseSummary: require('./parseSummary'),
|
||||
parseGlossary: require('./parseGlossary'),
|
||||
parseReadme: require('./parseReadme'),
|
||||
parseConfig: require('./parseConfig'),
|
||||
parsePagesList: require('./parsePagesList'),
|
||||
parseIgnore: require('./parseIgnore'),
|
||||
listAssets: require('./listAssets'),
|
||||
parseLanguages: require('./parseLanguages'),
|
||||
parsePage: require('./parsePage'),
|
||||
parsePageFromString: require('./parsePageFromString'),
|
||||
lookupStructureFile: require('./lookupStructureFile')
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ var findParsableFile = require('./findParsableFile');
|
||||
|
||||
@param {Book} book
|
||||
@param {String} type: one of ["glossary", "readme", "summary", "langs"]
|
||||
@return {Promise<File>} The path of the file found, relative
|
||||
@return {Promise<File | Undefined>} The path of the file found, relative
|
||||
to the book content root.
|
||||
*/
|
||||
function lookupStructureFile(book, type) {
|
||||
|
||||
+8
-16
@@ -1,27 +1,19 @@
|
||||
var Immutable = require('immutable');
|
||||
var fm = require('front-matter');
|
||||
var direction = require('direction');
|
||||
var parsePageFromString = require('./parsePageFromString');
|
||||
|
||||
/**
|
||||
Parse a page, read its content and parse the YAMl header
|
||||
|
||||
@param {Book} book
|
||||
@param {Page} page
|
||||
@return {Promise<Page>}
|
||||
*/
|
||||
* Parse a page, read its content and parse the YAMl header
|
||||
*
|
||||
* @param {Book} book
|
||||
* @param {Page} page
|
||||
* @return {Promise<Page>}
|
||||
*/
|
||||
function parsePage(book, page) {
|
||||
var fs = book.getContentFS();
|
||||
var file = page.getFile();
|
||||
|
||||
return fs.readAsString(file.getPath())
|
||||
.then(function(content) {
|
||||
var parsed = fm(content);
|
||||
|
||||
return page.merge({
|
||||
content: parsed.body,
|
||||
attributes: Immutable.fromJS(parsed.attributes),
|
||||
dir: direction(parsed.body)
|
||||
});
|
||||
return parsePageFromString(page, content);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
var Immutable = require('immutable');
|
||||
var fm = require('front-matter');
|
||||
var direction = require('direction');
|
||||
|
||||
/**
|
||||
* Parse a page, its content and the YAMl header
|
||||
*
|
||||
* @param {Page} page
|
||||
* @return {Page}
|
||||
*/
|
||||
function parsePageFromString(page, content) {
|
||||
var parsed = fm(content);
|
||||
|
||||
return page.merge({
|
||||
content: parsed.body,
|
||||
attributes: Immutable.fromJS(parsed.attributes),
|
||||
dir: direction(parsed.body)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
module.exports = parsePageFromString;
|
||||
@@ -1,9 +1,9 @@
|
||||
var jsonschema = require('jsonschema');
|
||||
var jsonSchemaDefaults = require('json-schema-defaults');
|
||||
var mergeDefaults = require('merge-defaults');
|
||||
|
||||
var schema = require('../constants/configSchema');
|
||||
var error = require('../utils/error');
|
||||
var mergeDefaults = require('../utils/mergeDefaults');
|
||||
|
||||
/**
|
||||
Validate a book.json content
|
||||
|
||||
+19
-17
@@ -4,20 +4,22 @@ var Immutable = require('immutable');
|
||||
var markdownParser = require('gitbook-markdown');
|
||||
var asciidocParser = require('gitbook-asciidoc');
|
||||
|
||||
var EXTENSIONS_MARKDOWN = require('./constants/extsMarkdown');
|
||||
var EXTENSIONS_ASCIIDOC = require('./constants/extsAsciidoc');
|
||||
var Parser = require('./models/parser');
|
||||
|
||||
// This list is ordered by priority of parsers to use
|
||||
var parsers = Immutable.List([
|
||||
Parser.create('markdown', ['.md', '.markdown', '.mdown'], markdownParser),
|
||||
Parser.create('asciidoc', ['.adoc', '.asciidoc'], asciidocParser)
|
||||
Parser.create('markdown', EXTENSIONS_MARKDOWN, markdownParser),
|
||||
Parser.create('asciidoc', EXTENSIONS_ASCIIDOC, asciidocParser)
|
||||
]);
|
||||
|
||||
/**
|
||||
Return a specific parser by its name
|
||||
|
||||
@param {String} name
|
||||
@return {Parser|undefined}
|
||||
*/
|
||||
* Return a specific parser by its name
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Parser|undefined}
|
||||
*/
|
||||
function getParser(name) {
|
||||
return parsers.find(function(parser) {
|
||||
return parser.getName() === name;
|
||||
@@ -25,11 +27,11 @@ function getParser(name) {
|
||||
}
|
||||
|
||||
/**
|
||||
Return a specific parser according to an extension
|
||||
|
||||
@param {String} ext
|
||||
@return {Parser|undefined}
|
||||
*/
|
||||
* Return a specific parser according to an extension
|
||||
*
|
||||
* @param {String} ext
|
||||
* @return {Parser|undefined}
|
||||
*/
|
||||
function getParserByExt(ext) {
|
||||
return parsers.find(function(parser) {
|
||||
return parser.matchExtension(ext);
|
||||
@@ -37,11 +39,11 @@ function getParserByExt(ext) {
|
||||
}
|
||||
|
||||
/**
|
||||
Return parser for a file
|
||||
|
||||
@param {String} ext
|
||||
@return {Parser|undefined}
|
||||
*/
|
||||
* Return parser for a file
|
||||
*
|
||||
* @param {String} ext
|
||||
* @return {Parser|undefined}
|
||||
*/
|
||||
function getParserForFile(filename) {
|
||||
return getParserByExt(path.extname(filename));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
var path = require('path');
|
||||
|
||||
var Book = require('../../models/book');
|
||||
var createNodeFS = require('../../fs/node');
|
||||
var findForBook = require('../findForBook');
|
||||
|
||||
describe('findForBook', function() {
|
||||
var fs = createNodeFS(
|
||||
path.resolve(__dirname, '../../..')
|
||||
);
|
||||
var book = Book.createForFS(fs);
|
||||
|
||||
it('should list default plugins', function() {
|
||||
return findForBook(book)
|
||||
.then(function(plugins) {
|
||||
expect(plugins.has('fontsettings')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
var PluginDependency = require('../../models/pluginDependency');
|
||||
var listAll = require('../listAll');
|
||||
|
||||
describe('listAll', function() {
|
||||
it('must list default', function() {
|
||||
var deps = PluginDependency.listFromString('ga,great');
|
||||
var plugins = listAll(deps);
|
||||
|
||||
expect(plugins.size).toBe(8);
|
||||
|
||||
expect(plugins.has('ga')).toBe(true);
|
||||
expect(plugins.has('great')).toBe(true);
|
||||
|
||||
expect(plugins.has('search')).toBe(true);
|
||||
});
|
||||
|
||||
it('must list from array with -', function() {
|
||||
var deps = PluginDependency.listFromString('ga,-great');
|
||||
var plugins = listAll(deps);
|
||||
|
||||
expect(plugins.size).toBe(7);
|
||||
|
||||
expect(plugins.has('ga')).toBe(true);
|
||||
expect(plugins.has('great')).toBe(false);
|
||||
});
|
||||
|
||||
it('must remove default plugins using -', function() {
|
||||
var deps = PluginDependency.listFromString('ga,-search');
|
||||
var plugins = listAll(deps);
|
||||
|
||||
expect(plugins.size).toBe(6);
|
||||
|
||||
expect(plugins.has('ga')).toBe(true);
|
||||
expect(plugins.has('search')).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
var PluginDependency = require('../../models/pluginDependency');
|
||||
var listDependencies = require('../listDependencies');
|
||||
var toNames = require('../toNames');
|
||||
|
||||
describe('listDependencies', function() {
|
||||
it('must list default', function() {
|
||||
var deps = PluginDependency.listFromString('ga,great');
|
||||
var plugins = listDependencies(deps);
|
||||
var names = toNames(plugins);
|
||||
|
||||
expect(names).toEqual([
|
||||
'ga', 'great',
|
||||
'highlight', 'search', 'lunr', 'sharing', 'fontsettings',
|
||||
'theme-default' ]);
|
||||
});
|
||||
|
||||
it('must list from array with -', function() {
|
||||
var deps = PluginDependency.listFromString('ga,-great');
|
||||
var plugins = listDependencies(deps);
|
||||
var names = toNames(plugins);
|
||||
|
||||
expect(names).toEqual([
|
||||
'ga',
|
||||
'highlight', 'search', 'lunr', 'sharing', 'fontsettings',
|
||||
'theme-default' ]);
|
||||
});
|
||||
|
||||
it('must remove default plugins using -', function() {
|
||||
var deps = PluginDependency.listFromString('ga,-search');
|
||||
var plugins = listDependencies(deps);
|
||||
var names = toNames(plugins);
|
||||
|
||||
expect(names).toEqual([
|
||||
'ga',
|
||||
'highlight', 'lunr', 'sharing', 'fontsettings',
|
||||
'theme-default' ]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
var path = require('path');
|
||||
var locateRootFolder = require('../locateRootFolder');
|
||||
|
||||
describe('locateRootFolder', function() {
|
||||
it('should correctly resolve the node_modules for gitbook', function() {
|
||||
expect(locateRootFolder()).toBe(
|
||||
path.resolve(__dirname, '../../../')
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
var PluginDependency = require('../../models/pluginDependency');
|
||||
var sortDependencies = require('../sortDependencies');
|
||||
var toNames = require('../toNames');
|
||||
|
||||
describe('sortDependencies', function() {
|
||||
it('must load themes after plugins', function() {
|
||||
var allPlugins = PluginDependency.listFromArray([
|
||||
'hello',
|
||||
'theme-test',
|
||||
'world'
|
||||
]);
|
||||
|
||||
var sorted = sortDependencies(allPlugins);
|
||||
var names = toNames(sorted);
|
||||
|
||||
expect(names).toEqual([
|
||||
'hello',
|
||||
'world',
|
||||
'theme-test'
|
||||
]);
|
||||
});
|
||||
|
||||
it('must keep order of themes', function() {
|
||||
var allPlugins = PluginDependency.listFromArray([
|
||||
'theme-test',
|
||||
'theme-test1',
|
||||
'hello',
|
||||
'theme-test2',
|
||||
'world'
|
||||
]);
|
||||
var sorted = sortDependencies(allPlugins);
|
||||
var names = toNames(sorted);
|
||||
|
||||
expect(names).toEqual([
|
||||
'hello',
|
||||
'world',
|
||||
'theme-test',
|
||||
'theme-test1',
|
||||
'theme-test2'
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,70 +0,0 @@
|
||||
var PluginDependency = require('../../models/pluginDependency');
|
||||
var sortPlugins = require('../sortPlugins');
|
||||
var listAll = require('../listAll');
|
||||
var THEME_PREFIX = require('../../constants/themePrefix');
|
||||
|
||||
|
||||
/**
|
||||
Check if a plugin is a theme given its name
|
||||
|
||||
@return {Boolean}
|
||||
*/
|
||||
function isTheme(name) {
|
||||
return (name && name.indexOf(THEME_PREFIX) === 0);
|
||||
}
|
||||
|
||||
describe('sortPlugins', function() {
|
||||
it('must load themes after plugins', function() {
|
||||
var deps = PluginDependency.listFromString('theme-faq'),
|
||||
allPlugins = listAll(deps);
|
||||
|
||||
return sortPlugins(allPlugins, [])
|
||||
.then(function(sorted) {
|
||||
var plugins = sorted.slice(0, -2),
|
||||
themes = sorted.slice(-2);
|
||||
|
||||
var pluginsOk = plugins.every(function(plugin) {
|
||||
return !isTheme(plugin.getName());
|
||||
});
|
||||
|
||||
var themesOk = themes.every(function(theme) {
|
||||
return isTheme(theme.getName());
|
||||
});
|
||||
|
||||
expect(pluginsOk).toBe(true);
|
||||
expect(plugins.has('search')).toBe(true);
|
||||
|
||||
expect(themesOk).toBe(true);
|
||||
expect(themes.size).toBe(2);
|
||||
expect(themes.has('theme-faq')).toBe(true);
|
||||
expect(themes.has('theme-default')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('must load themes after plugins with a complex dependencies list', function() {
|
||||
var deps = PluginDependency.listFromString('comment,theme-faq,-search,ga'),
|
||||
allPlugins = listAll(deps);
|
||||
|
||||
return sortPlugins(allPlugins, [])
|
||||
.then(function(sorted) {
|
||||
var plugins = sorted.slice(0, -2),
|
||||
themes = sorted.slice(-2);
|
||||
|
||||
var pluginsOk = plugins.every(function(plugin) {
|
||||
return !isTheme(plugin.getName());
|
||||
});
|
||||
|
||||
var themesOk = themes.every(function(theme) {
|
||||
return isTheme(theme.getName());
|
||||
});
|
||||
|
||||
expect(pluginsOk).toBe(true);
|
||||
expect(plugins.has('search')).toBe(false);
|
||||
|
||||
expect(themesOk).toBe(true);
|
||||
expect(themes.size).toBe(2);
|
||||
expect(themes.has('theme-faq')).toBe(true);
|
||||
expect(themes.has('theme-default')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,22 +1,22 @@
|
||||
var path = require('path');
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var Promise = require('../utils/promise');
|
||||
var timing = require('../utils/timing');
|
||||
var findInstalled = require('./findInstalled');
|
||||
var locateRootFolder = require('./locateRootFolder');
|
||||
|
||||
/**
|
||||
List all plugins installed in a book
|
||||
|
||||
@param {Book}
|
||||
@return {Promise<OrderedMap<String:Plugin>>}
|
||||
*/
|
||||
* List all plugins installed in a book
|
||||
*
|
||||
* @param {Book}
|
||||
* @return {Promise<OrderedMap<String:Plugin>>}
|
||||
*/
|
||||
function findForBook(book) {
|
||||
return timing.measure(
|
||||
'plugins.findForBook',
|
||||
|
||||
Promise.all([
|
||||
findInstalled(path.resolve(__dirname, '../..')),
|
||||
findInstalled(locateRootFolder()),
|
||||
findInstalled(book.getRoot())
|
||||
])
|
||||
|
||||
|
||||
@@ -8,21 +8,21 @@ var Plugin = require('../models/plugin');
|
||||
var PREFIX = require('../constants/pluginPrefix');
|
||||
|
||||
/**
|
||||
Validate if a package name is a GitBook plugin
|
||||
|
||||
@return {Boolean}
|
||||
*/
|
||||
* Validate if a package name is a GitBook plugin
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function validateId(name) {
|
||||
return name && name.indexOf(PREFIX) === 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
List all packages installed inside a folder
|
||||
|
||||
@param {String} folder
|
||||
@return {OrderedMap<String:Plugin>}
|
||||
*/
|
||||
* List all packages installed inside a folder
|
||||
*
|
||||
* @param {String} folder
|
||||
* @return {OrderedMap<String:Plugin>}
|
||||
*/
|
||||
function findInstalled(folder) {
|
||||
var options = {
|
||||
dev: false,
|
||||
@@ -31,7 +31,7 @@ function findInstalled(folder) {
|
||||
};
|
||||
var results = Immutable.OrderedMap();
|
||||
|
||||
function onPackage(pkg, isRoot) {
|
||||
function onPackage(pkg, parent) {
|
||||
if (!pkg.name) return;
|
||||
|
||||
var name = pkg.name;
|
||||
@@ -43,18 +43,19 @@ function findInstalled(folder) {
|
||||
var pluginName = name.slice(PREFIX.length);
|
||||
|
||||
if (!validateId(name)){
|
||||
if (!isRoot) return;
|
||||
if (parent) return;
|
||||
} else {
|
||||
results = results.set(pluginName, Plugin({
|
||||
name: pluginName,
|
||||
version: version,
|
||||
path: pkgPath,
|
||||
depth: depth
|
||||
depth: depth,
|
||||
parent: parent
|
||||
}));
|
||||
}
|
||||
|
||||
Immutable.Map(dependencies).forEach(function(dep) {
|
||||
onPackage(dep);
|
||||
onPackage(dep, pluginName);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -77,7 +78,7 @@ function findInstalled(folder) {
|
||||
var module_folder = path.join(node_modules, module);
|
||||
return Promise.nfcall(readInstalled, module_folder, options)
|
||||
.then(function(data) {
|
||||
onPackage(data, true);
|
||||
onPackage(data);
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
var npmi = require('npmi');
|
||||
|
||||
var pkg = require('../../package.json');
|
||||
var DEFAULT_PLUGINS = require('../constants/defaultPlugins');
|
||||
var Promise = require('../utils/promise');
|
||||
var installPlugin = require('./installPlugin');
|
||||
|
||||
|
||||
/**
|
||||
Install plugin requirements for a book
|
||||
|
||||
@@ -20,13 +18,17 @@ function installPlugins(book) {
|
||||
// Remove default plugins
|
||||
// (only if version is same as installed)
|
||||
plugins = plugins.filterNot(function(plugin) {
|
||||
var dependency = DEFAULT_PLUGINS.find(function(dep) {
|
||||
return dep.getName() === plugin.getName();
|
||||
});
|
||||
|
||||
return (
|
||||
// Disabled plugin
|
||||
!plugin.isEnabled() ||
|
||||
|
||||
// Or default one installed in GitBook itself
|
||||
(DEFAULT_PLUGINS.includes(plugin.getName()) &&
|
||||
plugin.getVersion() === pkg.dependencies[plugin.getNpmID()])
|
||||
(dependency &&
|
||||
plugin.getVersion() === dependency.getVersion())
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
var Immutable = require('immutable');
|
||||
var Plugin = require('../models/plugin');
|
||||
|
||||
var pkg = require('../../package.json');
|
||||
var DEFAULT_PLUGINS = require('../constants/defaultPlugins');
|
||||
|
||||
/**
|
||||
List all plugins for a book
|
||||
|
||||
@param {List<PluginDependency>} deps
|
||||
@return {OrderedMap<Plugin>}
|
||||
*/
|
||||
function listAll(deps) {
|
||||
// Extract list of plugins to disable (starting with -)
|
||||
var toRemove = deps
|
||||
.filter(function(plugin) {
|
||||
return !plugin.isEnabled();
|
||||
})
|
||||
.map(function(plugin) {
|
||||
return plugin.getName();
|
||||
});
|
||||
|
||||
// Convert to an ordered map of Plugin
|
||||
var plugins = deps
|
||||
.map(function(dep) {
|
||||
var plugin = Plugin.createFromDep(dep);
|
||||
|
||||
return [dep.getName(), plugin];
|
||||
});
|
||||
plugins = Immutable.OrderedMap(plugins);
|
||||
|
||||
// Append default plugins
|
||||
DEFAULT_PLUGINS.forEach(function(pluginName) {
|
||||
if (plugins.has(pluginName)) return;
|
||||
|
||||
plugins = plugins.set(pluginName, new Plugin({
|
||||
name: pluginName,
|
||||
version: pkg.dependencies[Plugin.nameToNpmID(pluginName)]
|
||||
}));
|
||||
});
|
||||
|
||||
// Remove plugins
|
||||
plugins = plugins.filterNot(function(plugin, name) {
|
||||
return toRemove.includes(name);
|
||||
});
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
module.exports = listAll;
|
||||
@@ -0,0 +1,33 @@
|
||||
var DEFAULT_PLUGINS = require('../constants/defaultPlugins');
|
||||
var sortDependencies = require('./sortDependencies');
|
||||
|
||||
/**
|
||||
* List all dependencies for a book, including default plugins.
|
||||
* It returns a concat with default plugins and remove disabled ones.
|
||||
*
|
||||
* @param {List<PluginDependency>} deps
|
||||
* @return {List<PluginDependency>}
|
||||
*/
|
||||
function listDependencies(deps) {
|
||||
// Extract list of plugins to disable (starting with -)
|
||||
var toRemove = deps
|
||||
.filter(function(plugin) {
|
||||
return !plugin.isEnabled();
|
||||
})
|
||||
.map(function(plugin) {
|
||||
return plugin.getName();
|
||||
});
|
||||
|
||||
// Concat with default plugins
|
||||
deps = deps.concat(DEFAULT_PLUGINS);
|
||||
|
||||
// Remove plugins
|
||||
deps = deps.filterNot(function(plugin) {
|
||||
return toRemove.includes(plugin.getName());
|
||||
});
|
||||
|
||||
// Sort
|
||||
return sortDependencies(deps);
|
||||
}
|
||||
|
||||
module.exports = listDependencies;
|
||||
@@ -0,0 +1,18 @@
|
||||
var listDependencies = require('./listDependencies');
|
||||
|
||||
/**
|
||||
* List all plugin requirements for a book.
|
||||
* It can be different from the final list of plugins,
|
||||
* since plugins can have their own dependencies
|
||||
*
|
||||
* @param {Book}
|
||||
* @return {List<PluginDependency>}
|
||||
*/
|
||||
function listDepsForBook(book) {
|
||||
var config = book.getConfig();
|
||||
var plugins = config.getPluginDependencies();
|
||||
|
||||
return listDependencies(plugins);
|
||||
}
|
||||
|
||||
module.exports = listDepsForBook;
|
||||
@@ -1,18 +0,0 @@
|
||||
var listAll = require('./listAll');
|
||||
|
||||
/**
|
||||
List all plugin requirements for a book.
|
||||
It can be different from the final list of plugins,
|
||||
since plugins can have their own dependencies
|
||||
|
||||
@param {Book}
|
||||
@return {OrderedMap<Plugin>}
|
||||
*/
|
||||
function listForBook(book) {
|
||||
var config = book.getConfig();
|
||||
var plugins = config.getPluginDependencies();
|
||||
|
||||
return listAll(plugins);
|
||||
}
|
||||
|
||||
module.exports = listForBook;
|
||||
+48
-31
@@ -1,52 +1,69 @@
|
||||
var Promise = require('../utils/promise');
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var listForBook = require('./listForBook');
|
||||
var Promise = require('../utils/promise');
|
||||
var listDepsForBook = require('./listDepsForBook');
|
||||
var findForBook = require('./findForBook');
|
||||
var loadPlugin = require('./loadPlugin');
|
||||
var sortPlugins = require('./sortPlugins');
|
||||
|
||||
|
||||
/**
|
||||
Load a list of plugins in a book
|
||||
|
||||
@param {Book}
|
||||
@return {Promise<Map<String:Plugin>}
|
||||
*/
|
||||
* Load all plugins in a book
|
||||
*
|
||||
* @param {Book}
|
||||
* @return {Promise<Map<String:Plugin>}
|
||||
*/
|
||||
function loadForBook(book) {
|
||||
var logger = book.getLogger();
|
||||
var requirements = listForBook(book);
|
||||
var requirementsKeys = requirements.keySeq().toList();
|
||||
|
||||
// List the dependencies
|
||||
var requirements = listDepsForBook(book);
|
||||
|
||||
// List all plugins installed in the book
|
||||
return findForBook(book)
|
||||
.then(function(installed) {
|
||||
// Filter out plugins not listed of first level
|
||||
// (aka pre-installed plugins)
|
||||
installed = installed.filter(function(plugin) {
|
||||
return (
|
||||
plugin.getDepth() > 0 ||
|
||||
requirements.has(plugin.getName())
|
||||
);
|
||||
});
|
||||
.then(function(installedMap) {
|
||||
var missing = [];
|
||||
var plugins = requirements.reduce(function(result, dep) {
|
||||
var name = dep.getName();
|
||||
var installed = installedMap.get(name);
|
||||
|
||||
if (installed) {
|
||||
var deps = installedMap
|
||||
.filter(function(plugin) {
|
||||
return plugin.getParent() === name;
|
||||
})
|
||||
.toArray();
|
||||
|
||||
result = result.concat(deps);
|
||||
result.push(installed);
|
||||
} else {
|
||||
missing.push(name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}, []);
|
||||
|
||||
// Convert plugins list to a map
|
||||
plugins = Immutable.List(plugins)
|
||||
.map(function(plugin) {
|
||||
return [
|
||||
plugin.getName(),
|
||||
plugin
|
||||
];
|
||||
});
|
||||
plugins = Immutable.OrderedMap(plugins);
|
||||
|
||||
return sortPlugins(installed, requirementsKeys);
|
||||
})
|
||||
.then(function(installed) {
|
||||
// Log state
|
||||
logger.info.ln(installed.size + ' plugins are installed');
|
||||
if (requirements.size != installed.size) {
|
||||
logger.info.ln(installedMap.size + ' plugins are installed');
|
||||
if (requirements.size != installedMap.size) {
|
||||
logger.info.ln(requirements.size + ' explicitly listed');
|
||||
}
|
||||
|
||||
// Verify that all plugins are present
|
||||
var notInstalled = requirementsKeys.filter(function(name) {
|
||||
return !installed.has(name);
|
||||
});
|
||||
|
||||
if (notInstalled.size > 0) {
|
||||
throw new Error('Couldn\'t locate plugins "' + notInstalled.join(', ') + '", Run \'gitbook install\' to install plugins from registry.');
|
||||
if (missing.length > 0) {
|
||||
throw new Error('Couldn\'t locate plugins "' + missing.join(', ') + '", Run \'gitbook install\' to install plugins from registry.');
|
||||
}
|
||||
|
||||
return Promise.map(installed, function(plugin) {
|
||||
return Promise.map(plugins, function(plugin) {
|
||||
return loadPlugin(book, plugin);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
var path = require('path');
|
||||
var resolve = require('resolve');
|
||||
|
||||
var DEFAULT_PLUGINS = require('../constants/defaultPlugins');
|
||||
|
||||
/**
|
||||
* Resolve the root folder containing for node_modules
|
||||
* since gitbook can be used as a library and dependency can be flattened.
|
||||
*
|
||||
* @return {String} folderPath
|
||||
*/
|
||||
function locateRootFolder() {
|
||||
var firstDefaultPlugin = DEFAULT_PLUGINS.first();
|
||||
var pluginPath = resolve.sync(firstDefaultPlugin.getNpmID() + '/package.json', {
|
||||
basedir: __dirname
|
||||
});
|
||||
var nodeModules = path.resolve(pluginPath, '../../..');
|
||||
|
||||
return nodeModules;
|
||||
}
|
||||
|
||||
module.exports = locateRootFolder;
|
||||
@@ -0,0 +1,34 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var THEME_PREFIX = require('../constants/themePrefix');
|
||||
|
||||
var TYPE_PLUGIN = 'plugin';
|
||||
var TYPE_THEME = 'theme';
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type of a plugin given its name
|
||||
* @param {Plugin} plugin
|
||||
* @return {String}
|
||||
*/
|
||||
function pluginType(plugin) {
|
||||
var name = plugin.getName();
|
||||
return (name && name.indexOf(THEME_PREFIX) === 0) ? TYPE_THEME : TYPE_PLUGIN;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort the list of dependencies to match list in book.json
|
||||
* The themes should always be loaded after the plugins
|
||||
*
|
||||
* @param {List<PluginDependency>} deps
|
||||
* @return {List<PluginDependency>}
|
||||
*/
|
||||
function sortDependencies(plugins) {
|
||||
var byTypes = plugins.groupBy(pluginType);
|
||||
|
||||
return byTypes.get(TYPE_PLUGIN, Immutable.List())
|
||||
.concat(byTypes.get(TYPE_THEME, Immutable.List()));
|
||||
}
|
||||
|
||||
module.exports = sortDependencies;
|
||||
@@ -1,45 +0,0 @@
|
||||
var Promise = require('../utils/promise');
|
||||
|
||||
var THEME_PREFIX = require('../constants/themePrefix');
|
||||
var LOADING_ORDER = ['plugin', 'theme'];
|
||||
|
||||
/**
|
||||
Returns the type of a plugin given its name
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
function pluginType(name) {
|
||||
return (name && name.indexOf(THEME_PREFIX) === 0) ? 'theme' : 'plugin';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Sort the list of installed plugins to match list in book.json
|
||||
The themes should always be loaded after the plugins
|
||||
|
||||
@param {<OrderedMap<String:Plugin>>} plugins
|
||||
@param {<List<String>>} requirementsKeys
|
||||
@return {Promise<OrderedMap<String:Plugin>>}
|
||||
*/
|
||||
|
||||
function sortPlugins(plugins, requirementsKeys) {
|
||||
return Promise()
|
||||
.then(function() {
|
||||
// Sort plugins to match list in book.json
|
||||
plugins = plugins.sort(function(a, b) {
|
||||
// Get order from book.json
|
||||
var definitionOrder = requirementsKeys.indexOf(a.getName()) < requirementsKeys.indexOf(b.getName());
|
||||
|
||||
// Get order from plugins a and b type
|
||||
var aType = pluginType(a.getName()),
|
||||
bType = pluginType(b.getName()),
|
||||
loadingOrder = LOADING_ORDER.indexOf(aType) < LOADING_ORDER.indexOf(bType);
|
||||
|
||||
return loadingOrder || definitionOrder ? -1 : 1;
|
||||
});
|
||||
|
||||
return plugins;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = sortPlugins;
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
/**
|
||||
* Return list of plugin names. This method is nly used in unit tests.
|
||||
*
|
||||
* @param {OrderedMap<String:Plugin} plugins
|
||||
* @return {Array<String>}
|
||||
*/
|
||||
function toNames(plugins) {
|
||||
return plugins
|
||||
.map(function(plugin) {
|
||||
return plugin.getName();
|
||||
})
|
||||
.toArray();
|
||||
}
|
||||
|
||||
module.exports = toNames;
|
||||
@@ -1,10 +1,10 @@
|
||||
var Immutable = require('immutable');
|
||||
var jsonschema = require('jsonschema');
|
||||
var jsonSchemaDefaults = require('json-schema-defaults');
|
||||
var mergeDefaults = require('merge-defaults');
|
||||
|
||||
var Promise = require('../utils/promise');
|
||||
var error = require('../utils/error');
|
||||
var mergeDefaults = require('../utils/mergeDefaults');
|
||||
|
||||
/**
|
||||
Validate one plugin for a book and update book's confiration
|
||||
|
||||
@@ -75,6 +75,12 @@ describe('ConrefsLoader', function() {
|
||||
|
||||
describe('transform', function() {
|
||||
function transform(filePath, source) {
|
||||
expect(filePath).toBeA('string');
|
||||
expect(source).toBeA('string');
|
||||
|
||||
expect(filePath).toBe(path.resolve(__dirname, 'include.md'));
|
||||
expect(source).toBe('Hello World');
|
||||
|
||||
return 'test-' + source + '-endtest';
|
||||
}
|
||||
var engine = TemplateEngine({
|
||||
|
||||
@@ -8,16 +8,15 @@ var PathUtils = require('../utils/path');
|
||||
|
||||
|
||||
/**
|
||||
Template loader resolving both:
|
||||
- relative url ("./test.md")
|
||||
- absolute url ("/test.md")
|
||||
- git url ("")
|
||||
|
||||
|
||||
@param {String} rootFolder
|
||||
@param {Function(filePath, source)} transformFn (optional)
|
||||
@param {Logger} logger (optional)
|
||||
*/
|
||||
* Template loader resolving both:
|
||||
* - relative url ("./test.md")
|
||||
* - absolute url ("/test.md")
|
||||
* - git url ("")
|
||||
*
|
||||
* @param {String} rootFolder
|
||||
* @param {Function(filePath, source)} transformFn (optional)
|
||||
* @param {Logger} logger (optional)
|
||||
*/
|
||||
var ConrefsLoader = nunjucks.Loader.extend({
|
||||
async: true,
|
||||
|
||||
@@ -43,6 +42,8 @@ var ConrefsLoader = nunjucks.Loader.extend({
|
||||
// Read file from absolute path
|
||||
return fs.readFile(filepath)
|
||||
.then(function(source) {
|
||||
source = source.toString('utf8');
|
||||
|
||||
if (that.transformFn) {
|
||||
return that.transformFn(filepath, source);
|
||||
}
|
||||
@@ -51,7 +52,7 @@ var ConrefsLoader = nunjucks.Loader.extend({
|
||||
})
|
||||
.then(function(source) {
|
||||
return {
|
||||
src: source.toString('utf8'),
|
||||
src: source,
|
||||
path: filepath
|
||||
};
|
||||
});
|
||||
|
||||
@@ -2,13 +2,13 @@ var Immutable = require('immutable');
|
||||
var parsers = require('../parsers');
|
||||
|
||||
/**
|
||||
Return a list of all shortcuts that can apply
|
||||
to a file for a TemplatEngine
|
||||
|
||||
@param {List<TemplateBlock>} engine
|
||||
@param {String} filePath
|
||||
@return {List<TemplateShortcut>}
|
||||
*/
|
||||
* Return a list of all shortcuts that can apply
|
||||
* to a file for a TemplatEngine
|
||||
*
|
||||
* @param {List<TemplateBlock>} engine
|
||||
* @param {String} filePath
|
||||
* @return {List<TemplateShortcut>}
|
||||
*/
|
||||
function listShortcuts(blocks, filePath) {
|
||||
var parser = parsers.getForFile(filePath);
|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@ var error = require('../utils/error');
|
||||
var render = require('./render');
|
||||
|
||||
/**
|
||||
Render a template
|
||||
|
||||
@param {TemplateEngine} engine
|
||||
@param {String} filePath
|
||||
@param {Object} context
|
||||
@return {Promise<TemplateOutput>}
|
||||
*/
|
||||
* Render a template
|
||||
*
|
||||
* @param {TemplateEngine} engine
|
||||
* @param {String} filePath
|
||||
* @param {Object} context
|
||||
* @return {Promise<TemplateOutput>}
|
||||
*/
|
||||
function renderTemplateFile(engine, filePath, context) {
|
||||
var loader = engine.getLoader();
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
var escapeStringRegexp = require('escape-string-regexp');
|
||||
var listShortcuts = require('./listShortcuts');
|
||||
|
||||
/*
|
||||
Apply a shortcut of block to a template
|
||||
@param {String} content
|
||||
@param {Shortcut} shortcut
|
||||
@return {String}
|
||||
*/
|
||||
/**
|
||||
* Apply a shortcut of block to a template
|
||||
* @param {String} content
|
||||
* @param {Shortcut} shortcut
|
||||
* @return {String}
|
||||
*/
|
||||
function applyShortcut(content, shortcut) {
|
||||
var start = shortcut.getStart();
|
||||
var end = shortcut.getEnd();
|
||||
@@ -24,13 +24,13 @@ function applyShortcut(content, shortcut) {
|
||||
}
|
||||
|
||||
/**
|
||||
Replace shortcuts from blocks in a string
|
||||
|
||||
@param {List<TemplateBlock>} engine
|
||||
@param {String} filePath
|
||||
@param {String} content
|
||||
@return {String}
|
||||
*/
|
||||
* Replace shortcuts from blocks in a string
|
||||
*
|
||||
* @param {List<TemplateBlock>} engine
|
||||
* @param {String} filePath
|
||||
* @param {String} content
|
||||
* @return {String}
|
||||
*/
|
||||
function replaceShortcuts(blocks, filePath, content) {
|
||||
var shortcuts = listShortcuts(blocks, filePath);
|
||||
return shortcuts.reduce(applyShortcut, content);
|
||||
|
||||
@@ -12,11 +12,11 @@ var ThemesLoader = nunjucks.Loader.extend({
|
||||
.map(path.normalize);
|
||||
},
|
||||
|
||||
/*
|
||||
Read source of a resolved filepath
|
||||
@param {String}
|
||||
@return {Object}
|
||||
*/
|
||||
/**
|
||||
* Read source of a resolved filepath
|
||||
* @param {String}
|
||||
* @return {Object}
|
||||
*/
|
||||
getSource: function(fullpath) {
|
||||
if (!fullpath) return null;
|
||||
|
||||
@@ -40,19 +40,19 @@ var ThemesLoader = nunjucks.Loader.extend({
|
||||
};
|
||||
},
|
||||
|
||||
/*
|
||||
Nunjucks calls "isRelative" to determine when to call "resolve".
|
||||
We handle absolute paths ourselves in ".resolve" so we always return true
|
||||
*/
|
||||
/**
|
||||
* Nunjucks calls "isRelative" to determine when to call "resolve".
|
||||
* We handle absolute paths ourselves in ".resolve" so we always return true
|
||||
*/
|
||||
isRelative: function() {
|
||||
return true;
|
||||
},
|
||||
|
||||
/*
|
||||
Get original search path containing a template
|
||||
@param {String} filepath
|
||||
@return {String} searchPath
|
||||
*/
|
||||
/**
|
||||
* Get original search path containing a template
|
||||
* @param {String} filepath
|
||||
* @return {String} searchPath
|
||||
*/
|
||||
getSearchPath: function(filepath) {
|
||||
return this.searchPaths
|
||||
.sortBy(function(s) {
|
||||
@@ -63,22 +63,22 @@ var ThemesLoader = nunjucks.Loader.extend({
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
Get template name from a filepath
|
||||
@param {String} filepath
|
||||
@return {String} name
|
||||
*/
|
||||
/**
|
||||
* Get template name from a filepath
|
||||
* @param {String} filepath
|
||||
* @return {String} name
|
||||
*/
|
||||
getTemplateName: function(filepath) {
|
||||
var originalSearchPath = this.getSearchPath(filepath);
|
||||
return originalSearchPath? path.relative(originalSearchPath, filepath) : null;
|
||||
},
|
||||
|
||||
/*
|
||||
Resolve a template from a current template
|
||||
@param {String|null} from
|
||||
@param {String} to
|
||||
@return {String|null}
|
||||
*/
|
||||
/**
|
||||
* Resolve a template from a current template
|
||||
* @param {String|null} from
|
||||
* @param {String} to
|
||||
* @return {String|null}
|
||||
*/
|
||||
resolve: function(from, to) {
|
||||
var searchPaths = this.searchPaths;
|
||||
|
||||
|
||||
@@ -39,6 +39,21 @@ describe('LocationUtils', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('.flatten', function() {
|
||||
it('should remove leading slash', function() {
|
||||
expect(LocationUtils.flatten('/test.md')).toBe('test.md');
|
||||
expect(LocationUtils.flatten('/hello/cool.md')).toBe('hello/cool.md');
|
||||
});
|
||||
|
||||
it('should remove leading slashes', function() {
|
||||
expect(LocationUtils.flatten('///test.md')).toBe('test.md');
|
||||
});
|
||||
|
||||
it('should not break paths', function() {
|
||||
expect(LocationUtils.flatten('hello/cool.md')).toBe('hello/cool.md');
|
||||
});
|
||||
});
|
||||
|
||||
describe('.toAbsolute', function() {
|
||||
it('should correctly transform as absolute', function() {
|
||||
expect(LocationUtils.toAbsolute('http://google.fr')).toBe('http://google.fr');
|
||||
|
||||
+51
-35
@@ -40,13 +40,28 @@ function normalize(s) {
|
||||
}
|
||||
|
||||
/**
|
||||
Convert a relative path to absolute
|
||||
* Flatten a path, it removes the leading "/"
|
||||
*
|
||||
* @param {String} href
|
||||
* @return {String}
|
||||
*/
|
||||
function flatten(href) {
|
||||
href = normalize(href);
|
||||
if (href[0] == '/') {
|
||||
href = normalize(href.slice(1));
|
||||
}
|
||||
|
||||
@param {String} href
|
||||
@param {String} dir: directory parent of the file currently in rendering process
|
||||
@param {String} outdir: directory parent from the html output
|
||||
@return {String}
|
||||
*/
|
||||
return href;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a relative path to absolute
|
||||
*
|
||||
* @param {String} href
|
||||
* @param {String} dir: directory parent of the file currently in rendering process
|
||||
* @param {String} outdir: directory parent from the html output
|
||||
* @return {String}
|
||||
*/
|
||||
function toAbsolute(_href, dir, outdir) {
|
||||
if (isExternal(_href) || isDataURI(_href)) {
|
||||
return _href;
|
||||
@@ -74,50 +89,51 @@ function toAbsolute(_href, dir, outdir) {
|
||||
}
|
||||
|
||||
/**
|
||||
Convert an absolute path to a relative path for a specific folder (dir)
|
||||
('test/', 'hello.md') -> '../hello.md'
|
||||
|
||||
@param {String} dir: current directory
|
||||
@param {String} file: absolute path of file
|
||||
@return {String}
|
||||
*/
|
||||
* Convert an absolute path to a relative path for a specific folder (dir)
|
||||
* ('test/', 'hello.md') -> '../hello.md'
|
||||
*
|
||||
* @param {String} dir: current directory
|
||||
* @param {String} file: absolute path of file
|
||||
* @return {String}
|
||||
*/
|
||||
function relative(dir, file) {
|
||||
var isDirectory = file.slice(-1) === '/';
|
||||
return normalize(path.relative(dir, file)) + (isDirectory? '/': '');
|
||||
}
|
||||
|
||||
/**
|
||||
Convert an absolute path to a relative path for a specific folder (dir)
|
||||
('test/test.md', 'hello.md') -> '../hello.md'
|
||||
|
||||
@param {String} baseFile: current file
|
||||
@param {String} file: absolute path of file
|
||||
@return {String}
|
||||
*/
|
||||
* Convert an absolute path to a relative path for a specific folder (dir)
|
||||
* ('test/test.md', 'hello.md') -> '../hello.md'
|
||||
*
|
||||
* @param {String} baseFile: current file
|
||||
* @param {String} file: absolute path of file
|
||||
* @return {String}
|
||||
*/
|
||||
function relativeForFile(baseFile, file) {
|
||||
return relative(path.dirname(baseFile), file);
|
||||
}
|
||||
|
||||
/**
|
||||
Compare two paths, return true if they are identical
|
||||
('README.md', './README.md') -> true
|
||||
|
||||
@param {String} p1: first path
|
||||
@param {String} p2: second path
|
||||
@return {Boolean}
|
||||
*/
|
||||
* Compare two paths, return true if they are identical
|
||||
* ('README.md', './README.md') -> true
|
||||
*
|
||||
* @param {String} p1: first path
|
||||
* @param {String} p2: second path
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function areIdenticalPaths(p1, p2) {
|
||||
return normalize(p1) === normalize(p2);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
areIdenticalPaths: areIdenticalPaths,
|
||||
isDataURI: isDataURI,
|
||||
isExternal: isExternal,
|
||||
isRelative: isRelative,
|
||||
isAnchor: isAnchor,
|
||||
normalize: normalize,
|
||||
toAbsolute: toAbsolute,
|
||||
relative: relative,
|
||||
relativeForFile: relativeForFile
|
||||
isDataURI: isDataURI,
|
||||
isExternal: isExternal,
|
||||
isRelative: isRelative,
|
||||
isAnchor: isAnchor,
|
||||
normalize: normalize,
|
||||
toAbsolute: toAbsolute,
|
||||
relative: relative,
|
||||
relativeForFile: relativeForFile,
|
||||
flatten: flatten
|
||||
};
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
/**
|
||||
* Merge
|
||||
* @param {Object|Map} obj
|
||||
* @param {Object|Map} src
|
||||
* @return {Object}
|
||||
*/
|
||||
function mergeDefaults(obj, src) {
|
||||
var objValue = Immutable.fromJS(obj);
|
||||
var srcValue = Immutable.fromJS(src);
|
||||
|
||||
return srcValue.mergeDeep(objValue).toJS();
|
||||
}
|
||||
|
||||
module.exports = mergeDefaults;
|
||||
+44
-43
@@ -2,34 +2,35 @@ var Q = require('q');
|
||||
var Immutable = require('immutable');
|
||||
|
||||
// Debugging for long stack traces
|
||||
if (global.__DEV__ || process.env.DEBUG) {
|
||||
if (process.env.DEBUG || process.env.CI) {
|
||||
Q.longStackSupport = true;
|
||||
}
|
||||
|
||||
/**
|
||||
Reduce an array to a promise
|
||||
|
||||
@param {Array|List} arr
|
||||
@param {Function(value, element, index)}
|
||||
@return {Promise<Mixed>}
|
||||
*/
|
||||
* Reduce an array to a promise
|
||||
*
|
||||
* @param {Array|List} arr
|
||||
* @param {Function(value, element, index)}
|
||||
* @return {Promise<Mixed>}
|
||||
*/
|
||||
function reduce(arr, iter, base) {
|
||||
arr = Immutable.Iterable.isIterable(arr)? arr : Immutable.List(arr);
|
||||
|
||||
return arr.reduce(function(prev, elem, key) {
|
||||
return prev.then(function(val) {
|
||||
return prev
|
||||
.then(function(val) {
|
||||
return iter(val, elem, key);
|
||||
});
|
||||
}, Q(base));
|
||||
}
|
||||
|
||||
/**
|
||||
Iterate over an array using an async iter
|
||||
|
||||
@param {Array|List} arr
|
||||
@param {Function(value, element, index)}
|
||||
@return {Promise}
|
||||
*/
|
||||
* Iterate over an array using an async iter
|
||||
*
|
||||
* @param {Array|List} arr
|
||||
* @param {Function(value, element, index)}
|
||||
* @return {Promise}
|
||||
*/
|
||||
function forEach(arr, iter) {
|
||||
return reduce(arr, function(val, el, key) {
|
||||
return iter(el, key);
|
||||
@@ -37,12 +38,12 @@ function forEach(arr, iter) {
|
||||
}
|
||||
|
||||
/**
|
||||
Transform an array
|
||||
|
||||
@param {Array|List} arr
|
||||
@param {Function(value, element, index)}
|
||||
@return {Promise}
|
||||
*/
|
||||
* Transform an array
|
||||
*
|
||||
* @param {Array|List} arr
|
||||
* @param {Function(value, element, index)}
|
||||
* @return {Promise}
|
||||
*/
|
||||
function serie(arr, iter, base) {
|
||||
return reduce(arr, function(before, item, key) {
|
||||
return Q(iter(item, key))
|
||||
@@ -54,12 +55,12 @@ function serie(arr, iter, base) {
|
||||
}
|
||||
|
||||
/**
|
||||
Iter over an array and return first result (not null)
|
||||
|
||||
@param {Array|List} arr
|
||||
@param {Function(element, index)}
|
||||
@return {Promise<Mixed>}
|
||||
*/
|
||||
* Iter over an array and return first result (not null)
|
||||
*
|
||||
* @param {Array|List} arr
|
||||
* @param {Function(element, index)}
|
||||
* @return {Promise<Mixed>}
|
||||
*/
|
||||
function some(arr, iter) {
|
||||
arr = Immutable.List(arr);
|
||||
|
||||
@@ -73,12 +74,12 @@ function some(arr, iter) {
|
||||
}
|
||||
|
||||
/**
|
||||
Map an array using an async (promised) iterator
|
||||
|
||||
@param {Array|List} arr
|
||||
@param {Function(element, index)}
|
||||
@return {Promise<List>}
|
||||
*/
|
||||
* Map an array using an async (promised) iterator
|
||||
*
|
||||
* @param {Array|List} arr
|
||||
* @param {Function(element, index)}
|
||||
* @return {Promise<List>}
|
||||
*/
|
||||
function mapAsList(arr, iter) {
|
||||
return reduce(arr, function(prev, entry, i) {
|
||||
return Q(iter(entry, i))
|
||||
@@ -90,12 +91,12 @@ function mapAsList(arr, iter) {
|
||||
}
|
||||
|
||||
/**
|
||||
Map an array or map
|
||||
|
||||
@param {Array|List|Map|OrderedMap} arr
|
||||
@param {Function(element, key)}
|
||||
@return {Promise<List|Map|OrderedMap>}
|
||||
*/
|
||||
* Map an array or map
|
||||
*
|
||||
* @param {Array|List|Map|OrderedMap} arr
|
||||
* @param {Function(element, key)}
|
||||
* @return {Promise<List|Map|OrderedMap>}
|
||||
*/
|
||||
function map(arr, iter) {
|
||||
if (Immutable.Map.isMap(arr)) {
|
||||
var type = 'Map';
|
||||
@@ -122,11 +123,11 @@ function map(arr, iter) {
|
||||
|
||||
|
||||
/**
|
||||
Wrap a function in a promise
|
||||
|
||||
@param {Function} func
|
||||
@return {Funciton}
|
||||
*/
|
||||
* Wrap a function in a promise
|
||||
*
|
||||
* @param {Function} func
|
||||
* @return {Funciton}
|
||||
*/
|
||||
function wrap(func) {
|
||||
return function() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
/**
|
||||
* Reduce the difference between a map and its default version
|
||||
* @param {Map} defaultVersion
|
||||
* @param {Map} currentVersion
|
||||
* @return {Map} The properties of currentVersion that differs from defaultVersion
|
||||
*/
|
||||
function reducedObject(defaultVersion, currentVersion) {
|
||||
if(defaultVersion === undefined) {
|
||||
return currentVersion;
|
||||
}
|
||||
|
||||
return currentVersion.reduce(function(result, value, key) {
|
||||
var defaultValue = defaultVersion.get(key);
|
||||
|
||||
if (Immutable.Map.isMap(value)) {
|
||||
var diffs = reducedObject(defaultValue, value);
|
||||
|
||||
if (diffs.size > 0) {
|
||||
return result.set(key, diffs);
|
||||
}
|
||||
}
|
||||
|
||||
if (Immutable.is(defaultValue, value)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return result.set(key, value);
|
||||
}, Immutable.Map());
|
||||
}
|
||||
|
||||
module.exports = reducedObject;
|
||||
+10
-9
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "3.1.0",
|
||||
"version": "3.2.0",
|
||||
"homepage": "https://www.gitbook.com",
|
||||
"description": "Library and cmd utility to generate GitBooks",
|
||||
"main": "lib/index.js",
|
||||
@@ -20,26 +20,26 @@
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"extend": "^3.0.0",
|
||||
"fresh-require": "1.0.3",
|
||||
"front-matter": "2.0.8",
|
||||
"gitbook-asciidoc": "1.2.0",
|
||||
"gitbook-markdown": "1.3.0",
|
||||
"front-matter": "^2.1.0",
|
||||
"gitbook-asciidoc": "1.2.2",
|
||||
"gitbook-markdown": "2.0.1",
|
||||
"gitbook-plugin-fontsettings": "2.0.0",
|
||||
"gitbook-plugin-highlight": "2.0.2",
|
||||
"gitbook-plugin-livereload": "0.0.1",
|
||||
"gitbook-plugin-lunr": "1.1.0",
|
||||
"gitbook-plugin-lunr": "1.2.0",
|
||||
"gitbook-plugin-search": "2.2.1",
|
||||
"gitbook-plugin-sharing": "1.0.2",
|
||||
"gitbook-plugin-theme-default": "1.0.4",
|
||||
"gitbook-plugin-theme-default": "1.0.5",
|
||||
"github-slugid": "1.0.1",
|
||||
"graceful-fs": "4.1.4",
|
||||
"i18n-t": "1.0.0",
|
||||
"i18n-t": "1.0.1",
|
||||
"ignore": "3.1.2",
|
||||
"immutable": "^3.8.1",
|
||||
"is": "^3.1.0",
|
||||
"js-yaml": "^3.6.1",
|
||||
"json-schema-defaults": "0.1.1",
|
||||
"jsonschema": "1.1.0",
|
||||
"juice": "1.11.0",
|
||||
"merge-defaults": "0.2.1",
|
||||
"juice": "2.0.0",
|
||||
"mkdirp": "0.5.1",
|
||||
"moment": "2.13.0",
|
||||
"npm": "3.9.2",
|
||||
@@ -48,6 +48,7 @@
|
||||
"nunjucks-do": "1.0.0",
|
||||
"object-path": "^0.9.2",
|
||||
"omit-keys": "^0.1.0",
|
||||
"open": "0.0.5",
|
||||
"q": "1.4.1",
|
||||
"read-installed": "^4.0.3",
|
||||
"request": "2.72.0",
|
||||
|
||||
+19
-9
@@ -6,10 +6,9 @@ var cheerio = require('cheerio');
|
||||
|
||||
expect.extend({
|
||||
/**
|
||||
Check that a file is created in a directory:
|
||||
|
||||
expect('myFolder').toHaveFile('hello.md');
|
||||
*/
|
||||
* Check that a file is created in a directory:
|
||||
* expect('myFolder').toHaveFile('hello.md');
|
||||
*/
|
||||
toHaveFile: function(fileName) {
|
||||
var filePath = path.join(this.actual, fileName);
|
||||
var exists = fs.existsSync(filePath);
|
||||
@@ -36,8 +35,8 @@ expect.extend({
|
||||
},
|
||||
|
||||
/**
|
||||
Check that a value is defined (not null nor undefined)
|
||||
*/
|
||||
* Check that a value is defined (not null nor undefined)
|
||||
*/
|
||||
toBeDefined: function() {
|
||||
expect.assert(
|
||||
!(is.undefined(this.actual) || is.null(this.actual)),
|
||||
@@ -47,10 +46,21 @@ expect.extend({
|
||||
},
|
||||
|
||||
/**
|
||||
Check that a dom element exists in HTML
|
||||
* Check that a value is defined (not null nor undefined)
|
||||
*/
|
||||
toNotBeDefined: function() {
|
||||
expect.assert(
|
||||
(is.undefined(this.actual) || is.null(this.actual)),
|
||||
'expected %s to be not defined',
|
||||
this.actual
|
||||
);
|
||||
return this;
|
||||
},
|
||||
|
||||
@param {String} selector
|
||||
*/
|
||||
/**
|
||||
* Check that a dom element exists in HTML
|
||||
* @param {String} selector
|
||||
*/
|
||||
toHaveDOMElement: function(selector) {
|
||||
var $ = cheerio.load(this.actual);
|
||||
var $el = $(selector);
|
||||
|
||||
Reference in New Issue
Block a user