mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-29 05:29:02 +00:00
Compare commits
43 Commits
3.0.0-pre.15
...
3.0.3
| Author | SHA1 | Date | |
|---|---|---|---|
| ebe845d46c | |||
| edee8d2fc5 | |||
| 5f3de0876f | |||
| 2ceab04d01 | |||
| de37de44e8 | |||
| ccb87d5a13 | |||
| e2e0ca3f05 | |||
| b33a79704b | |||
| 6def174b84 | |||
| 346a18b444 | |||
| b32685c698 | |||
| 09b3618da8 | |||
| 901328f247 | |||
| b47ebaa9d9 | |||
| 465e8d6c0a | |||
| ecd86ec877 | |||
| 5d7864154c | |||
| 7b915428f7 | |||
| 0bce3517fd | |||
| 089815a8fd | |||
| 7e8f1a9632 | |||
| 8469648241 | |||
| e7a44358a5 | |||
| fb930d889f | |||
| 721a75309b | |||
| b856bff91b | |||
| 0744f000f9 | |||
| d1e226dba5 | |||
| 1923d28d71 | |||
| 484a306adb | |||
| 5bfc9f2f56 | |||
| 86a65e5287 | |||
| 956d301dd7 | |||
| 00d1a7b5a6 | |||
| 2718c9c017 | |||
| 3234c1e03f | |||
| c8f8eece92 | |||
| 7394efa9dc | |||
| 3e4241fc88 | |||
| a5ae79604c | |||
| fc4bdd68a1 | |||
| f83975b022 | |||
| 167d3957bf |
+14
-1
@@ -2,7 +2,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 3.0.0 (pre)
|
||||
## 3.0.3
|
||||
- Fix redirection in `gitbook serve` when accessing a folder without trailing slash
|
||||
- Fix links/annotations for glossary not being correctly resolved
|
||||
- Fix regression for supporting uppercase filenames in structure files
|
||||
- Fix `gitbook install` when using Git URLs for plugins
|
||||
|
||||
## 3.0.2
|
||||
- Fix crash for anchor links
|
||||
|
||||
## 3.0.1
|
||||
- Fix regression in link resolution when contain anchor
|
||||
- Fix `structure.<name>` configuration not supporting filenames with dots
|
||||
|
||||
## 3.0.0
|
||||
- Summary can contain external links and anchors (Fix [#776](https://github.com/GitbookIO/gitbook/issues/776))
|
||||
- Summary can contain differents entitled sections
|
||||
- Glossary is generated as a normal page
|
||||
|
||||
@@ -5,13 +5,18 @@ init:
|
||||
# Test against these versions of Node.js.
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "6"
|
||||
- nodejs_version: "5"
|
||||
- nodejs_version: "4"
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
# Install scripts. (runs after repo cloning)
|
||||
install:
|
||||
# Get the latest stable version of Node.js or io.js
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
- set CI=true
|
||||
# install svgexport
|
||||
- npm install svgexport -g
|
||||
# install modules
|
||||
@@ -27,3 +32,7 @@ test_script:
|
||||
|
||||
# Don't actually build.
|
||||
build: off
|
||||
|
||||
# Clone only last commit
|
||||
shallow_clone: true
|
||||
clone_depth: 1
|
||||
|
||||
@@ -7,6 +7,7 @@ GitBook allows you to customize your book using a flexible configuration. These
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `root` | Path to the root folder containing all the book's files, except `book.json`|
|
||||
| `structure` | To specify paths for Readme, Summary, Glossary etc. See [Structure paragraph](#structure). |
|
||||
| `title` | Title of your book, default value is extracted from the README. On GitBook.com this field is pre-filled. |
|
||||
| `description` | Description of your book, default value is extracted from the README. On GitBook.com this field is pre-filled. |
|
||||
| `author` | Name of the author. On GitBook.com this field is pre-filled. |
|
||||
@@ -32,6 +33,18 @@ Since version 3.0.0, GitBook can use themes. See [the theming section](themes/RE
|
||||
| -------- | ----------- |
|
||||
| `theme` | The theme to use for the book |
|
||||
|
||||
### Structure
|
||||
|
||||
In addition to the `root` variable, you can tell Gitbook the name of the files for Readme, Summary, Glossary, Languages (instead of using the default names such as `README.md`).
|
||||
These files must be at the root of your book (or the root of every language book). Paths such as `dir/MY_README.md` are not accepted.
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `structure.readme` | Readme file name (defaults to `README.md`) |
|
||||
| `structure.summary` | Summary file name (defaults to `SUMMARY.md`) |
|
||||
| `structure.glossary` | Glossary file name (defaults to `GLOSSARY.md`) |
|
||||
| `structure.languages` | Languages file name (defaults to `LANGS.md`) |
|
||||
|
||||
### PDF Options
|
||||
|
||||
PDF Output can be customized using a set of options in the `book.json`:
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ var filepath = output.resolve('myimage.png');
|
||||
var fileurl = output.toURL('mychapter/README.md');
|
||||
|
||||
// Write a file in the output folder
|
||||
output.write('hello.txt', 'Hello World')
|
||||
output.writeFile('hello.txt', 'Hello World')
|
||||
.then(function() { ... });
|
||||
|
||||
// Copy a file to the output folder
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ It integrates well with the [GitBook Editor](https://www.gitbook.com/editor).
|
||||
|
||||
Installing GitBook is easy and straightforward. Your system just needs to meet these two requirements:
|
||||
|
||||
* NodeJS (v4.0.0 and above are adviced)
|
||||
* NodeJS (v4.0.0 and above is recommended)
|
||||
* Windows, Linux, Unix, or Mac OS X
|
||||
|
||||
##### Install with NPM
|
||||
|
||||
@@ -51,5 +51,5 @@ This is the default content
|
||||
|
||||
# License
|
||||
|
||||
{% import "./LICENSE" %}
|
||||
{% include "./LICENSE" %}
|
||||
```
|
||||
|
||||
@@ -8,6 +8,7 @@ var deprecate = require('./deprecate');
|
||||
var fileToURL = require('../output/helper/fileToURL');
|
||||
var defaultBlocks = require('../constants/defaultBlocks');
|
||||
var gitbook = require('../gitbook');
|
||||
var parsers = require('../parsers');
|
||||
|
||||
var encodeConfig = require('./encodeConfig');
|
||||
var encodeSummary = require('./encodeSummary');
|
||||
@@ -95,6 +96,34 @@ function encodeGlobal(output) {
|
||||
return encodePage(output, page);
|
||||
},
|
||||
|
||||
/**
|
||||
Render a block of text (markdown/asciidoc)
|
||||
|
||||
@param {String} type
|
||||
@param {String} text
|
||||
@return {Promise<String>}
|
||||
*/
|
||||
renderBlock: function(type, text) {
|
||||
var parser = parsers.get(type);
|
||||
|
||||
return parser.parsePage(text)
|
||||
.get('content');
|
||||
},
|
||||
|
||||
/**
|
||||
Render an inline text (markdown/asciidoc)
|
||||
|
||||
@param {String} type
|
||||
@param {String} text
|
||||
@return {Promise<String>}
|
||||
*/
|
||||
renderInline: function(type, text) {
|
||||
var parser = parsers.get(type);
|
||||
|
||||
return parser.parseInline(text)
|
||||
.get('content');
|
||||
},
|
||||
|
||||
template: {
|
||||
/**
|
||||
Apply a templating block and returns its result
|
||||
@@ -142,6 +171,21 @@ function encodeGlobal(output) {
|
||||
return fileToURL(output, filePath);
|
||||
},
|
||||
|
||||
/**
|
||||
Check that a file exists.
|
||||
|
||||
@param {String} fileName
|
||||
@return {Promise}
|
||||
*/
|
||||
hasFile: function(fileName, content) {
|
||||
return Promise()
|
||||
.then(function() {
|
||||
var filePath = PathUtils.resolveInRoot(outputFolder, fileName);
|
||||
|
||||
return fs.exists(filePath);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
Write a file to the output folder,
|
||||
It creates the required folder
|
||||
@@ -160,6 +204,27 @@ function encodeGlobal(output) {
|
||||
return fs.writeFile(filePath, content);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
Copy a file to the output folder
|
||||
It creates the required folder.
|
||||
|
||||
@param {String} inputFile
|
||||
@param {String} outputFile
|
||||
@param {Buffer} content
|
||||
@return {Promise}
|
||||
*/
|
||||
copyFile: function(inputFile, outputFile, content) {
|
||||
return Promise()
|
||||
.then(function() {
|
||||
var outputFilePath = PathUtils.resolveInRoot(outputFolder, outputFile);
|
||||
|
||||
return fs.ensureFile(outputFilePath)
|
||||
.then(function() {
|
||||
return fs.copy(inputFile, outputFilePath);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
+5
-1
@@ -11,5 +11,9 @@ module.exports = {
|
||||
|
||||
// Modifiers
|
||||
SummaryModifier: Modifiers.Summary,
|
||||
ConfigModifier: Modifiers.Config
|
||||
ConfigModifier: Modifiers.Config,
|
||||
|
||||
// Constants
|
||||
CONFIG_FILES: require('./constants/configFiles.js'),
|
||||
IGNORE_FILES: require('./constants/ignoreFiles.js')
|
||||
};
|
||||
|
||||
+21
-2
@@ -71,11 +71,18 @@ Server.prototype.start = function(dir, port) {
|
||||
|
||||
// Redirect to directory's index.html
|
||||
function redirect() {
|
||||
var resultURL = urlTransform(req.url, function(parsed) {
|
||||
parsed.pathname += '/';
|
||||
return parsed;
|
||||
});
|
||||
|
||||
res.statusCode = 301;
|
||||
res.setHeader('Location', req.url + '/');
|
||||
res.end('Redirecting to ' + req.url + '/');
|
||||
res.setHeader('Location', resultURL);
|
||||
res.end('Redirecting to ' + resultURL);
|
||||
}
|
||||
|
||||
res.setHeader('X-Current-Location', req.url);
|
||||
|
||||
// Send file
|
||||
send(req, url.parse(req.url).pathname, {
|
||||
root: dir
|
||||
@@ -106,4 +113,16 @@ Server.prototype.start = function(dir, port) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
urlTransform is a helper function that allows a function to transform
|
||||
a url string in it's parsed form and returns the new url as a string
|
||||
|
||||
@param {String} uri
|
||||
@param {Function} fn
|
||||
@return {String}
|
||||
*/
|
||||
function urlTransform(uri, fn) {
|
||||
return url.format(fn(url.parse(uri)));
|
||||
}
|
||||
|
||||
module.exports = Server;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
var jsonschema = require('jsonschema');
|
||||
var schema = require('../configSchema');
|
||||
|
||||
describe('configSchema', function() {
|
||||
|
||||
function validate(cfg) {
|
||||
var v = new jsonschema.Validator();
|
||||
return v.validate(cfg, schema, {
|
||||
propertyName: 'config'
|
||||
});
|
||||
}
|
||||
|
||||
describe('structure', function() {
|
||||
|
||||
it('should accept dot in filename', function() {
|
||||
var result = validate({
|
||||
structure: {
|
||||
readme: 'book-intro.adoc'
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.errors.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should accept uppercase in filename', function() {
|
||||
var result = validate({
|
||||
structure: {
|
||||
readme: 'BOOK.adoc'
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.errors.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should not accept filepath', function() {
|
||||
var result = validate({
|
||||
structure: {
|
||||
readme: 'folder/myFile.md'
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.errors.length).toBe(1);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,5 @@
|
||||
var FILENAME_REGEX = '^[a-zA-Z-._\d,\s]+$';
|
||||
|
||||
module.exports = {
|
||||
'$schema': 'http://json-schema.org/schema#',
|
||||
'id': 'https://gitbook.com/schemas/book.json',
|
||||
@@ -57,25 +59,25 @@ module.exports = {
|
||||
'default': 'LANGS.md',
|
||||
'type': 'string',
|
||||
'title': 'File to use as languages index',
|
||||
'pattern': '^[0-9a-zA-Z ... ]+$'
|
||||
'pattern': FILENAME_REGEX
|
||||
},
|
||||
'readme': {
|
||||
'default': 'README.md',
|
||||
'type': 'string',
|
||||
'title': 'File to use as preface',
|
||||
'pattern': '^[0-9a-zA-Z ... ]+$'
|
||||
'pattern': FILENAME_REGEX
|
||||
},
|
||||
'glossary': {
|
||||
'default': 'GLOSSARY.md',
|
||||
'type': 'string',
|
||||
'title': 'File to use as glossary index',
|
||||
'pattern': '^[0-9a-zA-Z ... ]+$'
|
||||
'pattern': FILENAME_REGEX
|
||||
},
|
||||
'summary': {
|
||||
'default': 'SUMMARY.md',
|
||||
'type': 'string',
|
||||
'title': 'File to use as table of contents',
|
||||
'pattern': '^[0-9a-zA-Z ... ]+$'
|
||||
'pattern': FILENAME_REGEX
|
||||
}
|
||||
},
|
||||
'additionalProperties': false
|
||||
|
||||
@@ -42,21 +42,39 @@ describe('PluginDependency', function() {
|
||||
expect(plugin.getVersion()).toBe('git+ssh://samy@github.com/GitbookIO/plugin-ga.git');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('listToArray', function() {
|
||||
var list = PluginDependency.listToArray(Immutable.List([
|
||||
PluginDependency.createFromString('hello@1.0.0'),
|
||||
PluginDependency.createFromString('noversion'),
|
||||
PluginDependency.createFromString('-disabled')
|
||||
]));
|
||||
describe('listToArray', function() {
|
||||
it('must create an array from a list of plugin dependencies', function() {
|
||||
var list = PluginDependency.listToArray(Immutable.List([
|
||||
PluginDependency.createFromString('hello@1.0.0'),
|
||||
PluginDependency.createFromString('noversion'),
|
||||
PluginDependency.createFromString('-disabled')
|
||||
]));
|
||||
|
||||
expect(list).toEqual([
|
||||
'hello@1.0.0',
|
||||
'noversion',
|
||||
'-disabled'
|
||||
]);
|
||||
expect(list).toEqual([
|
||||
'hello@1.0.0',
|
||||
'noversion',
|
||||
'-disabled'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('listFromArray', function() {
|
||||
it('must create an array from a list of plugin dependencies', function() {
|
||||
var arr = Immutable.fromJS([
|
||||
'hello@1.0.0',
|
||||
{
|
||||
'name': 'plugin-ga',
|
||||
'version': 'git+ssh://samy@github.com/GitbookIO/plugin-ga.git'
|
||||
}
|
||||
]);
|
||||
var list = PluginDependency.listFromArray(arr);
|
||||
|
||||
expect(list.first().getName()).toBe('hello');
|
||||
expect(list.first().getVersion()).toBe('1.0.0');
|
||||
expect(list.last().getName()).toBe('plugin-ga');
|
||||
expect(list.last().getVersion()).toBe('git+ssh://samy@github.com/GitbookIO/plugin-ga.git');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
+6
-2
@@ -324,12 +324,16 @@ Book.prototype.getDefaultGlossaryPath = function() {
|
||||
*/
|
||||
Book.createFromParent = function createFromParent(parent, language) {
|
||||
var ignore = parent.getIgnore();
|
||||
var config = parent.getConfig();
|
||||
|
||||
// Set language in configuration
|
||||
config = config.setValue('language', language);
|
||||
|
||||
return new Book({
|
||||
// Inherits config. logegr and list of ignored files
|
||||
logger: parent.getLogger(),
|
||||
config: parent.getConfig(),
|
||||
ignore: Ignore().add(ignore),
|
||||
config: config,
|
||||
ignore: ignore,
|
||||
|
||||
language: language,
|
||||
fs: FS.reduceScope(parent.getContentFS(), language)
|
||||
|
||||
+1
-1
@@ -229,7 +229,7 @@ FS.prototype.listAllFiles = function(dirName, filterFn) {
|
||||
};
|
||||
|
||||
/**
|
||||
Find a file in a folder (case incensitive)
|
||||
Find a file in a folder (case insensitive)
|
||||
Return the found filename
|
||||
|
||||
@param {String} dirname
|
||||
|
||||
@@ -55,6 +55,11 @@ Parser.prototype.parsePage = function(content) {
|
||||
return Promise(page(content));
|
||||
};
|
||||
|
||||
Parser.prototype.parseInline = function(content) {
|
||||
var inline = this.get('inline');
|
||||
return Promise(inline(content));
|
||||
};
|
||||
|
||||
Parser.prototype.parseLanguages = function(content) {
|
||||
var langs = this.get('langs');
|
||||
return Promise(langs(content));
|
||||
|
||||
+4
-11
@@ -1,9 +1,10 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var TemplateBlock = require('./templateBlock');
|
||||
var PREFIX = require('../constants/pluginPrefix');
|
||||
var DEFAULT_VERSION = '*';
|
||||
|
||||
var PluginDependency = require('./pluginDependency');
|
||||
|
||||
var Plugin = Immutable.Record({
|
||||
name: String(),
|
||||
|
||||
@@ -53,7 +54,7 @@ Plugin.prototype.getDepth = function() {
|
||||
@return {String}
|
||||
*/
|
||||
Plugin.prototype.getNpmID = function() {
|
||||
return Plugin.nameToNpmID(this.getName());
|
||||
return PluginDependency.nameToNpmID(this.getName());
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -152,14 +153,6 @@ Plugin.createFromDep = function(dep) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Return NPM id for a plugin name
|
||||
|
||||
@param {String}
|
||||
@return {String}
|
||||
*/
|
||||
Plugin.nameToNpmID = function(s) {
|
||||
return PREFIX + s;
|
||||
};
|
||||
Plugin.nameToNpmID = PluginDependency.nameToNpmID;
|
||||
|
||||
module.exports = Plugin;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
var is = require('is');
|
||||
var semver = require('semver');
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var PREFIX = require('../constants/pluginPrefix');
|
||||
var DEFAULT_VERSION = '*';
|
||||
|
||||
/*
|
||||
@@ -29,6 +31,24 @@ PluginDependency.prototype.isEnabled = function() {
|
||||
return this.get('enabled');
|
||||
};
|
||||
|
||||
/**
|
||||
Return NPM ID for the dependency
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
PluginDependency.prototype.getNpmID = function() {
|
||||
return PluginDependency.nameToNpmID(this.getName());
|
||||
};
|
||||
|
||||
/**
|
||||
Is the plugin using a git dependency
|
||||
|
||||
@return {Boolean}
|
||||
*/
|
||||
PluginDependency.prototype.isGitDependency = function() {
|
||||
return !semver.validRange(this.getVersion());
|
||||
};
|
||||
|
||||
/**
|
||||
Create a plugin with a name and a plugin
|
||||
|
||||
@@ -95,8 +115,8 @@ PluginDependency.listFromArray = function(arr) {
|
||||
return PluginDependency.createFromString(entry);
|
||||
} else {
|
||||
return PluginDependency({
|
||||
name: entry.name,
|
||||
version: entry.version
|
||||
name: entry.get('name'),
|
||||
version: entry.get('version')
|
||||
});
|
||||
}
|
||||
})
|
||||
@@ -130,4 +150,14 @@ PluginDependency.listToArray = function(list) {
|
||||
.toJS();
|
||||
};
|
||||
|
||||
/**
|
||||
Return NPM id for a plugin name
|
||||
|
||||
@param {String}
|
||||
@return {String}
|
||||
*/
|
||||
PluginDependency.nameToNpmID = function(s) {
|
||||
return PREFIX + s;
|
||||
};
|
||||
|
||||
module.exports = PluginDependency;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
var fs = require('fs');
|
||||
var generateMock = require('../generateMock');
|
||||
var WebsiteGenerator = require('../website');
|
||||
|
||||
@@ -12,6 +13,54 @@ describe('WebsiteGenerator', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Glossary', function() {
|
||||
var folder;
|
||||
|
||||
before(function() {
|
||||
return generateMock(WebsiteGenerator, {
|
||||
'README.md': 'Hello World',
|
||||
'SUMMARY.md': '* [Deep](folder/page.md)',
|
||||
'folder': {
|
||||
'page.md': 'Hello World'
|
||||
},
|
||||
'GLOSSARY.md': '# Glossary\n\n## Hello\n\nHello World'
|
||||
})
|
||||
.then(function(_folder) {
|
||||
folder = _folder;
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate a GLOSSARY.html', function() {
|
||||
expect(folder).toHaveFile('GLOSSARY.html');
|
||||
});
|
||||
|
||||
it('should correctly resolve glossary links in README', function() {
|
||||
var html = fs.readFileSync(folder + '/index.html', 'utf8');
|
||||
expect(html).toHaveDOMElement('.page-inner a[href="GLOSSARY.html#hello"]');
|
||||
});
|
||||
|
||||
it('should correctly resolve glossary links in directory', function() {
|
||||
var html = fs.readFileSync(folder + '/folder/page.html', 'utf8');
|
||||
expect(html).toHaveDOMElement('.page-inner a[href="../GLOSSARY.html#hello"]');
|
||||
});
|
||||
|
||||
it('should accept a custom glossary file', function() {
|
||||
return generateMock(WebsiteGenerator, {
|
||||
'README.md': 'Hello World',
|
||||
'book.json': '{ "structure": { "glossary": "custom.md" } }',
|
||||
'custom.md': '# Glossary\n\n## Hello\n\nHello World'
|
||||
})
|
||||
.then(function(folder) {
|
||||
expect(folder).toHaveFile('custom.html');
|
||||
expect(folder).toNotHaveFile('GLOSSARY.html');
|
||||
|
||||
var html = fs.readFileSync(folder + '/index.html', 'utf8');
|
||||
expect(html).toHaveDOMElement('.page-inner a[href="custom.html#hello"]');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should copy asset files', function() {
|
||||
return generateMock(WebsiteGenerator, {
|
||||
'README.md': 'Hello World',
|
||||
|
||||
@@ -4,7 +4,6 @@ var Promise = require('../utils/promise');
|
||||
var error = require('../utils/error');
|
||||
var timing = require('../utils/timing');
|
||||
|
||||
var Parse = require('../parse');
|
||||
var Templating = require('../templating');
|
||||
var JSONUtils = require('../json');
|
||||
var createTemplateEngine = require('./createTemplateEngine');
|
||||
|
||||
@@ -4,6 +4,7 @@ var Api = require('../api');
|
||||
var Plugins = require('../plugins');
|
||||
var Promise = require('../utils/promise');
|
||||
var defaultBlocks = require('../constants/defaultBlocks');
|
||||
var fileToOutput = require('./helper/fileToOutput');
|
||||
|
||||
var CODEBLOCK = 'code';
|
||||
|
||||
@@ -17,9 +18,13 @@ function getModifiers(output, page) {
|
||||
var book = output.getBook();
|
||||
var plugins = output.getPlugins();
|
||||
var glossary = book.getGlossary();
|
||||
var entries = glossary.getEntries();
|
||||
var file = page.getFile();
|
||||
|
||||
// Glossary entries
|
||||
var entries = glossary.getEntries();
|
||||
var glossaryFile = glossary.getFile();
|
||||
var glossaryFilename = fileToOutput(output, glossaryFile.getPath());
|
||||
|
||||
// Current file path
|
||||
var currentFilePath = file.getPath();
|
||||
|
||||
@@ -34,18 +39,18 @@ function getModifiers(output, page) {
|
||||
// Normalize IDs on headings
|
||||
Modifiers.addHeadingId,
|
||||
|
||||
// Annotate text with glossary entries
|
||||
Modifiers.annotateText.bind(null, entries, glossaryFilename),
|
||||
|
||||
// Resolve images
|
||||
Modifiers.resolveImages.bind(null, currentFilePath),
|
||||
|
||||
// Resolve links (.md -> .html)
|
||||
Modifiers.resolveLinks.bind(null,
|
||||
currentFilePath,
|
||||
resolveFileToURL.bind(null, output)
|
||||
),
|
||||
|
||||
// Resolve images
|
||||
Modifiers.resolveImages.bind(null, currentFilePath),
|
||||
|
||||
// Annotate text with glossary entries
|
||||
Modifiers.annotateText.bind(null, entries),
|
||||
|
||||
// Highlight code blocks using "code" block
|
||||
Modifiers.highlightCode.bind(null, function(lang, source) {
|
||||
return Promise(code.applyBlock({
|
||||
|
||||
@@ -12,7 +12,7 @@ describe('annotateText', function() {
|
||||
it('should annotate text', function() {
|
||||
var $ = cheerio.load('<p>This is a word, and multiple words</p>');
|
||||
|
||||
annotateText(entries, $);
|
||||
annotateText(entries, 'GLOSSARY.md', $);
|
||||
|
||||
var links = $('a');
|
||||
expect(links.length).toBe(2);
|
||||
@@ -31,14 +31,14 @@ describe('annotateText', function() {
|
||||
it('should not annotate scripts', function() {
|
||||
var $ = cheerio.load('<script>This is a word, and multiple words</script>');
|
||||
|
||||
annotateText(entries, $);
|
||||
annotateText(entries, 'GLOSSARY.md', $);
|
||||
expect($('a').length).toBe(0);
|
||||
});
|
||||
|
||||
it('should not annotate when has class "no-glossary"', function() {
|
||||
var $ = cheerio.load('<p class="no-glossary">This is a word, and multiple words</p>');
|
||||
|
||||
annotateText(entries, $);
|
||||
annotateText(entries, 'GLOSSARY.md', $);
|
||||
expect($('a').length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ var resolveLinks = require('../resolveLinks');
|
||||
|
||||
describe('resolveLinks', function() {
|
||||
function resolveFileBasic(href) {
|
||||
return href;
|
||||
return 'fakeDir/' + href;
|
||||
}
|
||||
|
||||
function resolveFileCustom(href) {
|
||||
@@ -24,7 +24,7 @@ describe('resolveLinks', function() {
|
||||
return resolveLinks('hello.md', resolveFileBasic, $)
|
||||
.then(function() {
|
||||
var link = $('a');
|
||||
expect(link.attr('href')).toBe('test/cool.md');
|
||||
expect(link.attr('href')).toBe('fakeDir/test/cool.md');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,7 +34,31 @@ describe('resolveLinks', function() {
|
||||
return resolveLinks('afolder/hello.md', resolveFileBasic, $)
|
||||
.then(function() {
|
||||
var link = $('a');
|
||||
expect(link.attr('href')).toBe('../test/cool.md');
|
||||
expect(link.attr('href')).toBe('../fakeDir/test/cool.md');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Anchor', function() {
|
||||
it('should prevent anchors in resolution', function() {
|
||||
var TEST = '<p>This is a <a href="test/cool.md#an-anchor"></a></p>';
|
||||
var $ = cheerio.load(TEST);
|
||||
|
||||
return resolveLinks('hello.md', resolveFileCustom, $)
|
||||
.then(function() {
|
||||
var link = $('a');
|
||||
expect(link.attr('href')).toBe('test/cool.html#an-anchor');
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore pure anchor links', function() {
|
||||
var TEST = '<p>This is a <a href="#an-anchor"></a></p>';
|
||||
var $ = cheerio.load(TEST);
|
||||
|
||||
return resolveLinks('hello.md', resolveFileCustom, $)
|
||||
.then(function() {
|
||||
var link = $('a');
|
||||
expect(link.attr('href')).toBe('#an-anchor');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -62,9 +62,10 @@ function replaceText($, el, search, replace, text_only ) {
|
||||
Annotate text using a list of GlossaryEntry
|
||||
|
||||
@param {List<GlossaryEntry>}
|
||||
@param {String} glossaryFilePath
|
||||
@param {HTMLDom} $
|
||||
*/
|
||||
function annotateText(entries, $) {
|
||||
function annotateText(entries, glossaryFilePath, $) {
|
||||
entries.forEach(function(entry) {
|
||||
var entryId = entry.getID();
|
||||
var name = entry.getName();
|
||||
@@ -81,7 +82,7 @@ function annotateText(entries, $) {
|
||||
) return;
|
||||
|
||||
replaceText($, this, searchRegex, function(match) {
|
||||
return '<a href="/GLOSSARY.md#' + entryId + '" '
|
||||
return '<a href="/' + glossaryFilePath + '#' + entryId + '" '
|
||||
+ 'class="glossary-term" title="' + escape(description) + '">'
|
||||
+ match
|
||||
+ '</a>';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
var path = require('path');
|
||||
var url = require('url');
|
||||
|
||||
var LocationUtils = require('../../utils/location');
|
||||
var editHTMLElement = require('./editHTMLElement');
|
||||
@@ -27,14 +28,23 @@ function resolveLinks(currentFile, resolveFile, $) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calcul absolute path for this
|
||||
href = LocationUtils.toAbsolute(href, currentDirectory, '.');
|
||||
// Split anchor
|
||||
var parsed = url.parse(href);
|
||||
href = parsed.pathname || '';
|
||||
|
||||
// Resolve file
|
||||
href = resolveFile(href);
|
||||
if (href) {
|
||||
// Calcul absolute path for this
|
||||
href = LocationUtils.toAbsolute(href, currentDirectory, '.');
|
||||
|
||||
// Convert back to relative
|
||||
href = LocationUtils.relative(currentDirectory, href);
|
||||
// Resolve file
|
||||
href = resolveFile(href);
|
||||
|
||||
// Convert back to relative
|
||||
href = LocationUtils.relative(currentDirectory, href);
|
||||
}
|
||||
|
||||
// Add back anchor
|
||||
href = href + (parsed.hash || '');
|
||||
|
||||
$a.attr('href', href);
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ var parsers = require('../parsers');
|
||||
|
||||
@param {Book} book
|
||||
@param {String} filename
|
||||
@return {Promise<>}
|
||||
@return {Promise<File | Undefined>}
|
||||
*/
|
||||
function findParsableFile(book, filename) {
|
||||
var fs = book.getContentFS();
|
||||
|
||||
+11
-10
@@ -1,13 +1,14 @@
|
||||
|
||||
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')
|
||||
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')
|
||||
};
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
var findParsableFile = require('./findParsableFile');
|
||||
|
||||
/**
|
||||
Lookup a structure file (ex: SUMMARY.md, GLOSSARY.md) in a book. Uses
|
||||
book's config to find it.
|
||||
|
||||
@param {Book} book
|
||||
@param {String} type: one of ["glossary", "readme", "summary", "langs"]
|
||||
@return {Promise<File>} The path of the file found, relative
|
||||
to the book content root.
|
||||
*/
|
||||
function lookupStructureFile(book, type) {
|
||||
var config = book.getConfig();
|
||||
|
||||
var fileToSearch = config.getValue(['structure', type]);
|
||||
|
||||
return findParsableFile(book, fileToSearch);
|
||||
}
|
||||
|
||||
module.exports = lookupStructureFile;
|
||||
+18
-16
@@ -1,6 +1,23 @@
|
||||
var Promise = require('../utils/promise');
|
||||
var IGNORE_FILES = require('../constants/ignoreFiles');
|
||||
|
||||
var DEFAULT_IGNORES = [
|
||||
// Skip Git stuff
|
||||
'.git/',
|
||||
|
||||
// Skip OS X meta data
|
||||
'.DS_Store',
|
||||
|
||||
// Skip stuff installed by plugins
|
||||
'node_modules',
|
||||
|
||||
// Skip book outputs
|
||||
'_book',
|
||||
|
||||
// Ignore files in the templates folder
|
||||
'_layouts'
|
||||
];
|
||||
|
||||
/**
|
||||
Parse ignore files
|
||||
|
||||
@@ -15,22 +32,7 @@ function parseIgnore(book) {
|
||||
var fs = book.getFS();
|
||||
var ignore = book.getIgnore();
|
||||
|
||||
ignore = ignore.add([
|
||||
// Skip Git stuff
|
||||
'.git/',
|
||||
|
||||
// Skip OS X meta data
|
||||
'.DS_Store',
|
||||
|
||||
// Skip stuff installed by plugins
|
||||
'node_modules',
|
||||
|
||||
// Skip book outputs
|
||||
'_book',
|
||||
|
||||
// Ignore files in the templates folder
|
||||
'_layouts'
|
||||
]);
|
||||
ignore = ignore.add(DEFAULT_IGNORES);
|
||||
|
||||
return Promise.serie(IGNORE_FILES, function(filename) {
|
||||
return fs.readAsString(filename)
|
||||
|
||||
+41
-10
@@ -5,6 +5,25 @@ var Page = require('../models/page');
|
||||
var walkSummary = require('./walkSummary');
|
||||
var parsePage = require('./parsePage');
|
||||
|
||||
|
||||
/**
|
||||
Parse a page from a path
|
||||
|
||||
@param {Book} book
|
||||
@param {String} filePath
|
||||
@return {Page}
|
||||
*/
|
||||
function parseFilePage(book, filePath) {
|
||||
var fs = book.getContentFS();
|
||||
|
||||
return fs.statFile(filePath)
|
||||
.then(function(file) {
|
||||
var page = Page.createForFile(file);
|
||||
return parsePage(book, page);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Parse all pages from a book as an OrderedMap
|
||||
|
||||
@@ -12,10 +31,11 @@ var parsePage = require('./parsePage');
|
||||
@return {Promise<OrderedMap<Page>>}
|
||||
*/
|
||||
function parsePagesList(book) {
|
||||
var fs = book.getContentFS();
|
||||
var summary = book.getSummary();
|
||||
var glossary = book.getGlossary();
|
||||
var map = Immutable.OrderedMap();
|
||||
|
||||
// Parse pages from summary
|
||||
return timing.measure(
|
||||
'parse.listPages',
|
||||
walkSummary(summary, function(article) {
|
||||
@@ -26,21 +46,32 @@ function parsePagesList(book) {
|
||||
// Is the page ignored?
|
||||
if (book.isContentFileIgnored(filepath)) return;
|
||||
|
||||
return fs.statFile(filepath)
|
||||
.then(function(file) {
|
||||
var page = Page.createForFile(file);
|
||||
return parsePage(book, page);
|
||||
})
|
||||
return parseFilePage(book, filepath)
|
||||
.then(function(page) {
|
||||
map = map.set(filepath, page);
|
||||
}, function() {
|
||||
// file doesn't exist
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
return map;
|
||||
})
|
||||
);
|
||||
)
|
||||
|
||||
// Parse glossary
|
||||
.then(function() {
|
||||
var file = glossary.getFile();
|
||||
|
||||
if (!file.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return parseFilePage(book, file.getPath())
|
||||
.then(function(page) {
|
||||
map = map.set(file.getPath(), page);
|
||||
});
|
||||
})
|
||||
|
||||
.then(function() {
|
||||
return map;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
var findParsableFile = require('./findParsableFile');
|
||||
var Promise = require('../utils/promise');
|
||||
var error = require('../utils/error');
|
||||
var lookupStructureFile = require('./lookupStructureFile');
|
||||
|
||||
/**
|
||||
Parse a ParsableFile using a specific method
|
||||
@@ -55,11 +55,8 @@ function parseFile(fs, file, type) {
|
||||
*/
|
||||
function parseStructureFile(book, type) {
|
||||
var fs = book.getContentFS();
|
||||
var config = book.getConfig();
|
||||
|
||||
var fileToSearch = config.getValue(['structure', type]);
|
||||
|
||||
return findParsableFile(book, fileToSearch)
|
||||
return lookupStructureFile(book, type)
|
||||
.then(function(file) {
|
||||
if (!file) return [undefined, undefined];
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
var path = require('path');
|
||||
|
||||
var PluginDependency = require('../../models/pluginDependency');
|
||||
var Book = require('../../models/book');
|
||||
var NodeFS = require('../../fs/node');
|
||||
var installPlugin = require('../installPlugin');
|
||||
|
||||
var Parse = require('../../parse');
|
||||
|
||||
describe('installPlugin', function() {
|
||||
var book;
|
||||
|
||||
this.timeout(30000);
|
||||
|
||||
before(function() {
|
||||
var fs = NodeFS(path.resolve(__dirname, '../../../'));
|
||||
var baseBook = Book.createForFS(fs);
|
||||
|
||||
return Parse.parseConfig(baseBook)
|
||||
.then(function(_book) {
|
||||
book = _book;
|
||||
});
|
||||
});
|
||||
|
||||
it('must install a plugin from NPM', function() {
|
||||
var dep = PluginDependency.createFromString('ga');
|
||||
return installPlugin(book, dep);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
var path = require('path');
|
||||
|
||||
var Book = require('../../models/book');
|
||||
var NodeFS = require('../../fs/node');
|
||||
var installPlugins = require('../installPlugins');
|
||||
|
||||
var Parse = require('../../parse');
|
||||
|
||||
describe('installPlugins', function() {
|
||||
var book;
|
||||
|
||||
this.timeout(30000);
|
||||
|
||||
before(function() {
|
||||
var fs = NodeFS(path.resolve(__dirname, '../../../'));
|
||||
var baseBook = Book.createForFS(fs);
|
||||
|
||||
return Parse.parseConfig(baseBook)
|
||||
.then(function(_book) {
|
||||
book = _book;
|
||||
});
|
||||
});
|
||||
|
||||
it('must install all plugins from NPM', function() {
|
||||
return installPlugins(book)
|
||||
.then(function(n) {
|
||||
expect(n).toBe(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
var PluginDependency = require('../../models/pluginDependency');
|
||||
var resolveVersion = require('../resolveVersion');
|
||||
|
||||
describe('resolveVersion', function() {
|
||||
it('must skip resolving and return non-semver versions', function() {
|
||||
var plugin = PluginDependency.createFromString('ga@git+ssh://samy@github.com/GitbookIO/plugin-ga.git');
|
||||
|
||||
return resolveVersion(plugin)
|
||||
.then(function(version) {
|
||||
expect(version).toBe('git+ssh://samy@github.com/GitbookIO/plugin-ga.git');
|
||||
});
|
||||
});
|
||||
|
||||
it('must resolve a normal plugin dependency', function() {
|
||||
var plugin = PluginDependency.createFromString('ga@>0.9.0 < 1.0.1');
|
||||
|
||||
return resolveVersion(plugin)
|
||||
.then(function(version) {
|
||||
expect(version).toBe('1.0.0');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
var npmi = require('npmi');
|
||||
|
||||
var Promise = require('../utils/promise');
|
||||
var resolveVersion = require('./resolveVersion');
|
||||
|
||||
/**
|
||||
Install a plugin for a book
|
||||
|
||||
@param {Book}
|
||||
@param {PluginDependency}
|
||||
@return {Promise}
|
||||
*/
|
||||
function installPlugin(book, plugin) {
|
||||
var logger = book.getLogger();
|
||||
|
||||
var installFolder = book.getRoot();
|
||||
var name = plugin.getName();
|
||||
var requirement = plugin.getVersion();
|
||||
|
||||
logger.info.ln('');
|
||||
logger.info.ln('installing plugin "' + name + '"');
|
||||
|
||||
// Find a version to install
|
||||
return resolveVersion(plugin)
|
||||
.then(function(version) {
|
||||
if (!version) {
|
||||
throw new Error('Found no satisfactory version for plugin "' + name + '" with requirement "' + requirement + '"');
|
||||
}
|
||||
|
||||
logger.info.ln('install plugin "' + name +'" (' + requirement + ') from NPM with version', version);
|
||||
return Promise.nfcall(npmi, {
|
||||
'name': plugin.getNpmID(),
|
||||
'version': version,
|
||||
'path': installFolder,
|
||||
'npmLoad': {
|
||||
'loglevel': 'silent',
|
||||
'loaded': true,
|
||||
'prefix': installFolder
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
logger.info.ok('plugin "' + name + '" installed with success');
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = installPlugin;
|
||||
+12
-113
@@ -1,134 +1,32 @@
|
||||
var npm = require('npm');
|
||||
var npmi = require('npmi');
|
||||
var semver = require('semver');
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var pkg = require('../../package.json');
|
||||
var DEFAULT_PLUGINS = require('../constants/defaultPlugins');
|
||||
var Promise = require('../utils/promise');
|
||||
var Plugin = require('../models/plugin');
|
||||
var gitbook = require('../gitbook');
|
||||
var listForBook = require('./listForBook');
|
||||
|
||||
var npmIsReady;
|
||||
|
||||
/**
|
||||
Initialize and prepare NPM
|
||||
|
||||
@return {Promise}
|
||||
*/
|
||||
function initNPM() {
|
||||
if (npmIsReady) return npmIsReady;
|
||||
|
||||
npmIsReady = Promise.nfcall(npm.load, {
|
||||
silent: true,
|
||||
loglevel: 'silent'
|
||||
});
|
||||
|
||||
return npmIsReady;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Resolve a plugin to a version
|
||||
|
||||
@param {Plugin}
|
||||
@return {Promise<String>}
|
||||
*/
|
||||
function resolveVersion(plugin) {
|
||||
var npmId = Plugin.nameToNpmID(plugin.getName());
|
||||
var requiredVersion = plugin.getVersion();
|
||||
|
||||
return initNPM()
|
||||
.then(function() {
|
||||
return Promise.nfcall(npm.commands.view, [npmId + '@' + requiredVersion, 'engines'], true);
|
||||
})
|
||||
.then(function(versions) {
|
||||
versions = Immutable.Map(versions).entrySeq();
|
||||
|
||||
var result = versions
|
||||
.map(function(entry) {
|
||||
return {
|
||||
version: entry[0],
|
||||
gitbook: (entry[1].engines || {}).gitbook
|
||||
};
|
||||
})
|
||||
.filter(function(v) {
|
||||
return v.gitbook && gitbook.satisfies(v.gitbook);
|
||||
})
|
||||
.sort(function(v1, v2) {
|
||||
return semver.lt(v1.version, v2.version)? 1 : -1;
|
||||
})
|
||||
.get(0);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
} else {
|
||||
return result.version;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Install a plugin for a book
|
||||
|
||||
@param {Book}
|
||||
@param {Plugin}
|
||||
@return {Promise}
|
||||
*/
|
||||
function installPlugin(book, plugin) {
|
||||
var logger = book.getLogger();
|
||||
|
||||
var installFolder = book.getRoot();
|
||||
var name = plugin.getName();
|
||||
var requirement = plugin.getVersion();
|
||||
|
||||
logger.info.ln('');
|
||||
logger.info.ln('installing plugin "' + name + '"');
|
||||
|
||||
// Find a version to install
|
||||
return resolveVersion(plugin)
|
||||
.then(function(version) {
|
||||
if (!version) {
|
||||
throw new Error('Found no satisfactory version for plugin "' + name + '" with requirement "' + requirement + '"');
|
||||
}
|
||||
|
||||
logger.info.ln('install plugin "' + name +'" (' + requirement + ') from NPM with version', version);
|
||||
return Promise.nfcall(npmi, {
|
||||
'name': plugin.getNpmID(),
|
||||
'version': version,
|
||||
'path': installFolder,
|
||||
'npmLoad': {
|
||||
'loglevel': 'silent',
|
||||
'loaded': true,
|
||||
'prefix': installFolder
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
logger.info.ok('plugin "' + name + '" installed with success');
|
||||
});
|
||||
}
|
||||
var installPlugin = require('./installPlugin');
|
||||
|
||||
|
||||
/**
|
||||
Install plugin requirements for a book
|
||||
|
||||
@param {Book}
|
||||
@return {Promise}
|
||||
@return {Promise<Number>}
|
||||
*/
|
||||
function installPlugins(book) {
|
||||
var logger = book.getLogger();
|
||||
var plugins = listForBook(book);
|
||||
var config = book.getConfig();
|
||||
var plugins = config.getPluginDependencies();
|
||||
|
||||
// Remove default plugins
|
||||
// (only if version is same as installed)
|
||||
plugins = plugins.filterNot(function(plugin) {
|
||||
return (
|
||||
DEFAULT_PLUGINS.includes(plugin.getName()) &&
|
||||
plugin.getVersion() === pkg.dependencies[plugin.getNpmID()]
|
||||
// Disabled plugin
|
||||
!plugin.isEnabled() ||
|
||||
|
||||
// Or default one installed in GitBook itself
|
||||
(DEFAULT_PLUGINS.includes(plugin.getName()) &&
|
||||
plugin.getVersion() === pkg.dependencies[plugin.getNpmID()])
|
||||
);
|
||||
});
|
||||
|
||||
@@ -141,7 +39,8 @@ function installPlugins(book) {
|
||||
|
||||
return Promise.forEach(plugins, function(plugin) {
|
||||
return installPlugin(book, plugin);
|
||||
});
|
||||
})
|
||||
.thenResolve(plugins.size);
|
||||
}
|
||||
|
||||
module.exports = installPlugins;
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
var npm = require('npm');
|
||||
var semver = require('semver');
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var Promise = require('../utils/promise');
|
||||
var Plugin = require('../models/plugin');
|
||||
var gitbook = require('../gitbook');
|
||||
|
||||
var npmIsReady;
|
||||
|
||||
/**
|
||||
Initialize and prepare NPM
|
||||
|
||||
@return {Promise}
|
||||
*/
|
||||
function initNPM() {
|
||||
if (npmIsReady) return npmIsReady;
|
||||
|
||||
npmIsReady = Promise.nfcall(npm.load, {
|
||||
silent: true,
|
||||
loglevel: 'silent'
|
||||
});
|
||||
|
||||
return npmIsReady;
|
||||
}
|
||||
|
||||
/**
|
||||
Resolve a plugin dependency to a version
|
||||
|
||||
@param {PluginDependency} plugin
|
||||
@return {Promise<String>}
|
||||
*/
|
||||
function resolveVersion(plugin) {
|
||||
var npmId = Plugin.nameToNpmID(plugin.getName());
|
||||
var requiredVersion = plugin.getVersion();
|
||||
|
||||
if (plugin.isGitDependency()) {
|
||||
return Promise.resolve(requiredVersion);
|
||||
}
|
||||
|
||||
return initNPM()
|
||||
.then(function() {
|
||||
return Promise.nfcall(npm.commands.view, [npmId + '@' + requiredVersion, 'engines'], true);
|
||||
})
|
||||
.then(function(versions) {
|
||||
versions = Immutable.Map(versions).entrySeq();
|
||||
|
||||
var result = versions
|
||||
.map(function(entry) {
|
||||
return {
|
||||
version: entry[0],
|
||||
gitbook: (entry[1].engines || {}).gitbook
|
||||
};
|
||||
})
|
||||
.filter(function(v) {
|
||||
return v.gitbook && gitbook.satisfies(v.gitbook);
|
||||
})
|
||||
.sort(function(v1, v2) {
|
||||
return semver.lt(v1.version, v2.version)? 1 : -1;
|
||||
})
|
||||
.get(0);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
} else {
|
||||
return result.version;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = resolveVersion;
|
||||
@@ -8,7 +8,16 @@ function normalizePath(filename) {
|
||||
|
||||
// Return true if file path is inside a folder
|
||||
function isInRoot(root, filename) {
|
||||
root = path.normalize(root);
|
||||
filename = path.normalize(filename);
|
||||
|
||||
if (root === '.') {
|
||||
return true;
|
||||
}
|
||||
if (root[root.length - 1] != path.sep) {
|
||||
root = root + path.sep;
|
||||
}
|
||||
|
||||
return (filename.substr(0, root.length) === root);
|
||||
}
|
||||
|
||||
|
||||
+9
-9
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "3.0.0-pre.15",
|
||||
"version": "3.0.3",
|
||||
"homepage": "https://www.gitbook.com",
|
||||
"description": "Library and cmd utility to generate GitBooks",
|
||||
"main": "lib/index.js",
|
||||
@@ -8,7 +8,7 @@
|
||||
"dependencies": {
|
||||
"bash-color": "0.0.4",
|
||||
"cheerio": "0.20.0",
|
||||
"chokidar": "1.4.3",
|
||||
"chokidar": "1.5.0",
|
||||
"cp": "0.2.0",
|
||||
"cpr": "1.1.1",
|
||||
"crc": "3.4.0",
|
||||
@@ -20,7 +20,7 @@
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"extend": "^3.0.0",
|
||||
"fresh-require": "1.0.3",
|
||||
"front-matter": "2.0.7",
|
||||
"front-matter": "2.0.8",
|
||||
"gitbook-asciidoc": "1.1.4",
|
||||
"gitbook-markdown": "1.2.3",
|
||||
"gitbook-plugin-fontsettings": "1.0.3",
|
||||
@@ -29,20 +29,20 @@
|
||||
"gitbook-plugin-lunr": "1.1.0",
|
||||
"gitbook-plugin-search": "2.2.1",
|
||||
"gitbook-plugin-sharing": "1.0.2",
|
||||
"gitbook-plugin-theme-default": "1.0.0",
|
||||
"gitbook-plugin-theme-default": "1.0.3",
|
||||
"github-slugid": "1.0.1",
|
||||
"graceful-fs": "4.1.3",
|
||||
"graceful-fs": "4.1.4",
|
||||
"i18n-t": "1.0.0",
|
||||
"ignore": "3.1.2",
|
||||
"immutable": "^3.8.1",
|
||||
"is": "^3.1.0",
|
||||
"json-schema-defaults": "0.1.1",
|
||||
"jsonschema": "1.1.0",
|
||||
"juice": "1.10.0",
|
||||
"juice": "1.11.0",
|
||||
"merge-defaults": "0.2.1",
|
||||
"mkdirp": "0.5.1",
|
||||
"moment": "2.13.0",
|
||||
"npm": "3.8.8",
|
||||
"npm": "3.9.2",
|
||||
"npmi": "2.0.1",
|
||||
"nunjucks": "2.4.2",
|
||||
"nunjucks-do": "1.0.0",
|
||||
@@ -61,13 +61,13 @@
|
||||
"urijs": "1.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "2.9.0",
|
||||
"eslint": "2.10.2",
|
||||
"expect": "^1.20.1",
|
||||
"mocha": "^2.4.5"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"test": "./node_modules/.bin/mocha ./testing/setup.js './lib/**/*/__tests__/*.js' --bail --reporter=list --timeout=10000"
|
||||
"test": "./node_modules/.bin/mocha ./testing/setup.js \"./lib/**/*/__tests__/*.js\" --bail --reporter=list --timeout=10000"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -2,6 +2,7 @@ var is = require('is');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var expect = require('expect');
|
||||
var cheerio = require('cheerio');
|
||||
|
||||
expect.extend({
|
||||
/**
|
||||
@@ -43,6 +44,18 @@ expect.extend({
|
||||
'expected to be defined'
|
||||
);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
Check that a dom element exists in HTML
|
||||
|
||||
@param {String} selector
|
||||
*/
|
||||
toHaveDOMElement: function(selector) {
|
||||
var $ = cheerio.load(this.actual);
|
||||
var $el = $(selector);
|
||||
|
||||
expect.assert($el.length > 0, 'expected HTML to contains %s', selector);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user