mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-26 20:27:00 +00:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fb862f466f | |||
| 8d51300386 | |||
| c4b413563d | |||
| 8f852971f9 | |||
| 3c54c89464 | |||
| d07aa13822 | |||
| c218f7d0e3 | |||
| 72ad872e90 | |||
| 854f244d22 | |||
| 9acbb223db | |||
| c59fc687db | |||
| 0791b917f1 | |||
| ba182ce8e4 | |||
| 663291c515 | |||
| 99253eecea | |||
| 916a02cb36 | |||
| 114c4abcd1 | |||
| e1e3d11932 | |||
| df46217c78 | |||
| d18cc92396 | |||
| 8f63b4b790 | |||
| ce7de93936 | |||
| ef2a24fe6f | |||
| 038cd0155b | |||
| fb430ac1ed | |||
| 53cfa3703b | |||
| 7ed9e8d145 | |||
| 93e0b0cbea | |||
| 0118bd3168 | |||
| 07d5245a5f | |||
| 6b844213ec | |||
| dabdeb8a80 | |||
| ecd56315e6 | |||
| 15b9e839e9 | |||
| d7f7dcd47b | |||
| b1981e09b8 | |||
| c9edd0a795 | |||
| d83e0716bb |
@@ -19,6 +19,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Fix error in code highlighting for unknown languages
|
||||
- Accept SSH url as plugin version
|
||||
- Add templating blocks `markdown`, `asciidoc` and `markup`
|
||||
- Better search experience
|
||||
- Better default theme, more responsive and cleaner
|
||||
|
||||
## 2.6.7
|
||||
- Fix bug with filenames including spaces
|
||||
|
||||
@@ -6,11 +6,10 @@ module.exports = {
|
||||
title: 'GitBook Toolchain Documentation',
|
||||
|
||||
// Enforce use of GitBook v3
|
||||
gitbook: pkg.version,
|
||||
gitbook: '>=3.0.0-pre.0',
|
||||
|
||||
// Use the "official" theme
|
||||
plugins: ['theme-official', 'sitemap'],
|
||||
theme: 'official',
|
||||
|
||||
variables: {
|
||||
version: pkg.version
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ Each chapter has a dedicated page (`part#/README.md`) and is split into subchapt
|
||||
|
||||
##### Anchors
|
||||
|
||||
Chaptersi n the Table of Contents can be pointing to specific part of a file using anchor.
|
||||
Chapters in the Table of Contents can be pointing to specific part of a file using anchor.
|
||||
|
||||
```markdown
|
||||
# Summary
|
||||
|
||||
@@ -10,6 +10,8 @@ The following is a reference of the available data during book's parsing and the
|
||||
| `gitbook` | GitBook specific information |
|
||||
| `page` | Current page specific information |
|
||||
| `file` | File associated with the current page specific information |
|
||||
| `readme` | Information about the Readme |
|
||||
| `glossary` | Information about the Glossary |
|
||||
| `summary` | Information about the table of contents |
|
||||
| `languages` | List of languages for multi-lingual books |
|
||||
| `output` | Information about the output generator |
|
||||
@@ -71,3 +73,14 @@ Languages are defined by `{ id: 'en', title: 'English' }`.
|
||||
| `output.name` | Name of the output generator, possible values are `website`, `json`, `ebook` |
|
||||
| `output.format` | When `output.name == "ebook"`, `format` defines the ebook format that will be generated, possible values are `pdf`, `epub` or `mobi` |
|
||||
|
||||
### Readme Variables
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `readme.path` | Path to the Readme in the book |
|
||||
|
||||
### Glossary Variables
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `glossary.path` | Path to the Glossary in the book |
|
||||
|
||||
+19
-1
@@ -11,7 +11,25 @@ util.inherits(Readme, BackboneFile);
|
||||
|
||||
Readme.prototype.type = 'readme';
|
||||
|
||||
// Parse the readme content
|
||||
/*
|
||||
Return and extension of context to define the readme
|
||||
|
||||
@retrun {Object}
|
||||
*/
|
||||
Readme.prototype.getContext = function() {
|
||||
return {
|
||||
readme: {
|
||||
path: this.path
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
Parse the readme content
|
||||
|
||||
@param {String} content
|
||||
@retrun {Promise}
|
||||
*/
|
||||
Readme.prototype.parse = function(content) {
|
||||
var that = this;
|
||||
|
||||
|
||||
@@ -231,6 +231,7 @@ Summary.prototype.getContext = function() {
|
||||
|
||||
return {
|
||||
summary: {
|
||||
path: this.path,
|
||||
parts: _.map(this.parts, function(part) {
|
||||
return part.getContext(onArticle);
|
||||
})
|
||||
|
||||
+1
-10
@@ -97,16 +97,7 @@ function Book(opts) {
|
||||
Object.defineProperty(this, 'options', {
|
||||
get: function () {
|
||||
this.log.warn.ln('"options" property is deprecated, use config.get(key) instead');
|
||||
var cfg = this.config.dump();
|
||||
error.deprecateField(cfg, 'book', (this.output? this.output.name : null), '"options.generator" property is deprecated, use "output.name" instead');
|
||||
|
||||
// options.generator
|
||||
cfg.generator = this.output? this.output.name : null;
|
||||
|
||||
// options.output
|
||||
cfg.output = this.output? this.output.root() : null;
|
||||
|
||||
return cfg;
|
||||
return this.config.options;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ function watch(dir) {
|
||||
dir = path.resolve(dir);
|
||||
|
||||
var toWatch = [
|
||||
'book.json', 'book.js'
|
||||
'book.json', 'book.js', '_layouts/**'
|
||||
];
|
||||
|
||||
// Watch all parsable files
|
||||
|
||||
+6
-1
@@ -3,6 +3,7 @@ var semver = require('semver');
|
||||
|
||||
var gitbook = require('../gitbook');
|
||||
var Promise = require('../utils/promise');
|
||||
var error = require('../utils/error');
|
||||
var validator = require('./validator');
|
||||
var plugins = require('./plugins');
|
||||
|
||||
@@ -90,6 +91,9 @@ Config.prototype.replace = function(options) {
|
||||
return that.book.parent? that.book.parent.root : undefined;
|
||||
}
|
||||
});
|
||||
|
||||
error.deprecateField(this.options, 'generator', (this.book.output? this.book.output.name : null), '"options.generator" property is deprecated, use "output.name" instead');
|
||||
error.deprecateField(this.options, 'output', (this.book.output && this.book.output.root? this.book.output.root() : null), '"options.output" property is deprecated, use "output.root()" instead');
|
||||
};
|
||||
|
||||
// Return true if book has a configuration file
|
||||
@@ -119,7 +123,8 @@ Config.prototype.set = function(key, value) {
|
||||
|
||||
// Return a dump of the configuration
|
||||
Config.prototype.dump = function() {
|
||||
return _.cloneDeep(this.options);
|
||||
var opts = _.omit(this.options, 'generator', 'output');
|
||||
return _.cloneDeep(opts);
|
||||
};
|
||||
|
||||
// Return templating context
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
// Default plugins added to each books
|
||||
var DEFAULT_PLUGINS = ['highlight', 'search', 'sharing', 'fontsettings', 'theme-default'];
|
||||
var DEFAULT_PLUGINS = ['highlight', 'search', 'lunr', 'sharing', 'fontsettings', 'theme-default'];
|
||||
|
||||
// Return true if a plugin is a default plugin
|
||||
function isDefaultPlugin(name, version) {
|
||||
|
||||
+7
-4
@@ -38,10 +38,8 @@ function Output(book, opts, parent) {
|
||||
// Files to ignore in output
|
||||
this.ignore = Ignore();
|
||||
|
||||
// Hack to inherits from rules of the book:
|
||||
// Waiting for https://github.com/kaelzhang/node-ignore/pull/19 to be merged
|
||||
this.ignore._rules = this.ignore._rules.concat(this.book.ignore._rules);
|
||||
this.ignore._cache = {};
|
||||
// Hack to inherits from rules of the book
|
||||
this.ignore.add(this.book.ignore);
|
||||
}
|
||||
|
||||
// Default name for generator
|
||||
@@ -209,6 +207,10 @@ Output.prototype.onOutputSVG = function(page, svg) {
|
||||
// Output an image as a file
|
||||
// Normalize the relative link
|
||||
Output.prototype.onOutputImage = function(page, imgFile) {
|
||||
if (location.isExternal(imgFile)) {
|
||||
return imgFile;
|
||||
}
|
||||
|
||||
imgFile = page.resolveLocal(imgFile);
|
||||
return page.relative(imgFile);
|
||||
};
|
||||
@@ -246,6 +248,7 @@ Output.prototype.getContext = function() {
|
||||
},
|
||||
this.book.getContext(),
|
||||
(this.book.isLanguageBook()? this.book.parent: this.book).langs.getContext(),
|
||||
this.book.readme.getContext(),
|
||||
this.book.summary.getContext(),
|
||||
this.book.glossary.getContext(),
|
||||
this.book.config.getContext(),
|
||||
|
||||
+2
-8
@@ -44,13 +44,7 @@ EbookOutput.prototype.finish = function() {
|
||||
|
||||
// Generate SUMMARY.html
|
||||
.then(function() {
|
||||
return that.render('summary', that.getContext())
|
||||
.then(function(html) {
|
||||
return that.writeFile(
|
||||
'SUMMARY.html',
|
||||
html
|
||||
);
|
||||
});
|
||||
return that.render('summary', 'SUMMARY.html', that.getContext());
|
||||
})
|
||||
|
||||
// Start ebook-convert
|
||||
@@ -100,7 +94,7 @@ EbookOutput.prototype.getPDFTemplate = function(tpl) {
|
||||
this.getContext()
|
||||
);
|
||||
|
||||
return this.render('pdf_'+tpl, context)
|
||||
return this.renderAsString('pdf_'+tpl, context)
|
||||
|
||||
// Inline css, include css relative to the output folder
|
||||
.then(function(output) {
|
||||
|
||||
@@ -1,22 +1,14 @@
|
||||
var _ = require('lodash');
|
||||
var path = require('path');
|
||||
var util = require('util');
|
||||
var nunjucks = require('nunjucks');
|
||||
var I18n = require('i18n-t');
|
||||
|
||||
var Promise = require('../utils/promise');
|
||||
var location = require('../utils/location');
|
||||
var fs = require('../utils/fs');
|
||||
var defaultFilters = require('../template/filters');
|
||||
var FSLoader = require('../template/fs-loader');
|
||||
var conrefsLoader = require('./conrefs');
|
||||
var Output = require('./base');
|
||||
|
||||
|
||||
// Directory for a theme with the templates
|
||||
function templatesPath(dir) {
|
||||
return path.join(dir, '_layouts');
|
||||
}
|
||||
var Promise = require('../../utils/promise');
|
||||
var location = require('../../utils/location');
|
||||
var fs = require('../../utils/fs');
|
||||
var conrefsLoader = require('../conrefs');
|
||||
var Output = require('../base');
|
||||
var setupTemplateEnv = require('./templateEnv');
|
||||
|
||||
function _WebsiteOutput() {
|
||||
Output.apply(this, arguments);
|
||||
@@ -68,50 +60,7 @@ WebsiteOutput.prototype.prepare = function() {
|
||||
that.i18n.load(i18nRoot);
|
||||
});
|
||||
|
||||
that.env = new nunjucks.Environment(new FSLoader(_.map(searchPaths, templatesPath)));
|
||||
|
||||
// Add GitBook default filters
|
||||
_.each(defaultFilters, function(fn, filter) {
|
||||
that.env.addFilter(filter, fn);
|
||||
});
|
||||
|
||||
// Translate using _i18n locales
|
||||
that.env.addFilter('t', function(s) {
|
||||
return that.i18n.t(that.book.config.get('language'), s);
|
||||
});
|
||||
|
||||
// Transform an absolute path into a relative path
|
||||
// using this.ctx.page.path
|
||||
that.env.addFilter('resolveFile', function(href) {
|
||||
return location.normalize(that.resolveForPage(this.ctx.file.path, href));
|
||||
});
|
||||
|
||||
// Test if a file exists
|
||||
that.env.addFilter('fileExists', function(href) {
|
||||
return fs.existsSync(that.resolve(href));
|
||||
});
|
||||
|
||||
// Transform a '.md' into a '.html' (README -> index)
|
||||
that.env.addFilter('contentURL', function(s) {
|
||||
return that.toURL(s);
|
||||
});
|
||||
|
||||
// Relase path to an asset
|
||||
that.env.addFilter('resolveAsset', function(href) {
|
||||
href = path.join('gitbook', href);
|
||||
|
||||
// Resolve for current file
|
||||
if (this.ctx.file) {
|
||||
href = that.resolveForPage(this.ctx.file.path, '/' + href);
|
||||
}
|
||||
|
||||
// Use assets from parent
|
||||
if (that.book.isLanguageBook()) {
|
||||
href = path.join('../', href);
|
||||
}
|
||||
|
||||
return location.normalize(href);
|
||||
});
|
||||
that.searchPaths = searchPaths;
|
||||
})
|
||||
|
||||
// Copy assets from themes before copying files from book
|
||||
@@ -156,15 +105,7 @@ WebsiteOutput.prototype.onPage = function(page) {
|
||||
|
||||
// Render the page template with the same context as the json output
|
||||
.then(function() {
|
||||
return that.render('page', page.getOutputContext(that));
|
||||
})
|
||||
|
||||
// Write the HTML file
|
||||
.then(function(html) {
|
||||
return that.writeFile(
|
||||
that.outputPath(page.path),
|
||||
html
|
||||
);
|
||||
return that.render('page', that.outputPath(page.path), page.getOutputContext(that));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -196,25 +137,24 @@ WebsiteOutput.prototype.finish = function() {
|
||||
WebsiteOutput.prototype.outputMultilingualIndex = function() {
|
||||
var that = this;
|
||||
|
||||
return that.render('languages', that.getContext())
|
||||
.then(function(html) {
|
||||
return that.writeFile(
|
||||
'index.html',
|
||||
html
|
||||
);
|
||||
});
|
||||
return that.render('languages', 'index.html', that.getContext());
|
||||
};
|
||||
|
||||
// Render a template using nunjucks
|
||||
// Templates are stored in `_layouts` folders
|
||||
WebsiteOutput.prototype.render = function(tpl, context) {
|
||||
/*
|
||||
Render a template as an HTML string
|
||||
Templates are stored in `_layouts` folders
|
||||
|
||||
|
||||
@param {String} tpl: template name (ex: "page")
|
||||
@param {String} outputFile: filename to write, relative to the output
|
||||
@param {Object} context: context for the page
|
||||
@return {Promise}
|
||||
*/
|
||||
WebsiteOutput.prototype.renderAsString = function(tpl, context) {
|
||||
// Calcul template name
|
||||
var filename = this.templateName(tpl);
|
||||
|
||||
context = _.extend(context, {
|
||||
template: {
|
||||
self: filename
|
||||
},
|
||||
|
||||
plugins: {
|
||||
resources: this.resources
|
||||
},
|
||||
@@ -222,7 +162,56 @@ WebsiteOutput.prototype.render = function(tpl, context) {
|
||||
options: this.opts
|
||||
});
|
||||
|
||||
return Promise.nfcall(this.env.render.bind(this.env), filename, context);
|
||||
// Create environment
|
||||
var env = setupTemplateEnv(this, context);
|
||||
|
||||
return Promise.nfcall(env.render.bind(env), filename, context);
|
||||
};
|
||||
|
||||
/*
|
||||
Render a template using nunjucks
|
||||
Templates are stored in `_layouts` folders
|
||||
|
||||
|
||||
@param {String} tpl: template name (ex: "page")
|
||||
@param {String} outputFile: filename to write, relative to the output
|
||||
@param {Object} context: context for the page
|
||||
@return {Promise}
|
||||
*/
|
||||
WebsiteOutput.prototype.render = function(tpl, outputFile, context) {
|
||||
var that = this;
|
||||
|
||||
// Calcul relative path to the root
|
||||
var outputDirName = path.dirname(outputFile);
|
||||
var basePath = location.normalize(path.relative(outputDirName, './'));
|
||||
|
||||
// Setup complete context
|
||||
context = _.extend(context, {
|
||||
basePath: basePath,
|
||||
|
||||
template: {
|
||||
getJSContext: function() {
|
||||
return {
|
||||
page: _.omit(context.page, 'content'),
|
||||
config: context.config,
|
||||
file: context.file,
|
||||
gitbook: context.gitbook,
|
||||
basePath: basePath,
|
||||
book: {
|
||||
language: context.book.language
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return this.renderAsString(tpl, context)
|
||||
.then(function(html) {
|
||||
return that.writeFile(
|
||||
outputFile,
|
||||
html
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// Return a complete name for a template
|
||||
@@ -231,3 +220,6 @@ WebsiteOutput.prototype.templateName = function(name) {
|
||||
};
|
||||
|
||||
module.exports = WebsiteOutput;
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
var _ = require('lodash');
|
||||
var nunjucks = require('nunjucks');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var DoExtension = require('nunjucks-do')(nunjucks);
|
||||
|
||||
|
||||
var location = require('../../utils/location');
|
||||
var defaultFilters = require('../../template/filters');
|
||||
|
||||
var ThemeLoader = require('./themeLoader');
|
||||
|
||||
// Directory for a theme with the templates
|
||||
function templatesPath(dir) {
|
||||
return path.join(dir, '_layouts');
|
||||
}
|
||||
|
||||
/*
|
||||
Create and setup at Nunjucks template environment
|
||||
|
||||
@return {Nunjucks.Environment}
|
||||
*/
|
||||
function setupTemplateEnv(output, context) {
|
||||
var loader = new ThemeLoader(
|
||||
_.map(output.searchPaths, templatesPath)
|
||||
);
|
||||
var env = new nunjucks.Environment(loader);
|
||||
|
||||
env.addExtension('DoExtension', new DoExtension());
|
||||
|
||||
// Add context as global
|
||||
_.each(context, function(value, key) {
|
||||
env.addGlobal(key, value);
|
||||
});
|
||||
|
||||
// Add GitBook default filters
|
||||
_.each(defaultFilters, function(fn, filter) {
|
||||
env.addFilter(filter, fn);
|
||||
});
|
||||
|
||||
// Translate using _i18n locales
|
||||
env.addFilter('t', function t(s) {
|
||||
return output.i18n.t(output.book.config.get('language'), s);
|
||||
});
|
||||
|
||||
// Transform an absolute path into a relative path
|
||||
// using this.ctx.page.path
|
||||
env.addFilter('resolveFile', function resolveFile(href) {
|
||||
return location.normalize(output.resolveForPage(context.file.path, href));
|
||||
});
|
||||
|
||||
// Test if a file exists
|
||||
env.addFilter('fileExists', function fileExists(href) {
|
||||
return fs.existsSync(output.resolve(href));
|
||||
});
|
||||
|
||||
// Transform a '.md' into a '.html' (README -> index)
|
||||
env.addFilter('contentURL', function contentURL(s) {
|
||||
return output.toURL(s);
|
||||
});
|
||||
|
||||
// Get an article using its path
|
||||
env.addFilter('getArticleByPath', function getArticleByPath(s) {
|
||||
var article = output.book.summary.getArticle(s);
|
||||
if (!article) return undefined;
|
||||
|
||||
return article.getContext();
|
||||
});
|
||||
|
||||
// Relase path to an asset
|
||||
env.addFilter('resolveAsset', function resolveAsset(href) {
|
||||
href = path.join('gitbook', href);
|
||||
|
||||
// Resolve for current file
|
||||
if (context.file) {
|
||||
href = output.resolveForPage(context.file.path, '/' + href);
|
||||
}
|
||||
|
||||
// Use assets from parent
|
||||
if (output.book.isLanguageBook()) {
|
||||
href = path.join('../', href);
|
||||
}
|
||||
|
||||
return location.normalize(href);
|
||||
});
|
||||
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
module.exports = setupTemplateEnv;
|
||||
@@ -7,36 +7,86 @@ var nunjucks = require('nunjucks');
|
||||
Nunjucks loader similar to FileSystemLoader, but avoid infinite looping
|
||||
*/
|
||||
|
||||
/*
|
||||
Return true if a filename is relative.
|
||||
*/
|
||||
function isRelative(filename) {
|
||||
return (filename.indexOf('./') === 0 || filename.indexOf('../') === 0);
|
||||
}
|
||||
|
||||
var Loader = nunjucks.Loader.extend({
|
||||
var ThemeLoader = nunjucks.Loader.extend({
|
||||
init: function(searchPaths) {
|
||||
this.searchPaths = _.map(searchPaths, path.normalize);
|
||||
},
|
||||
|
||||
/*
|
||||
Read source of a resolved filepath
|
||||
|
||||
@param {String}
|
||||
@return {Object}
|
||||
*/
|
||||
getSource: function(fullpath) {
|
||||
if (!fullpath) return null;
|
||||
|
||||
fullpath = this.resolve(null, fullpath);
|
||||
var templateName = this.getTemplateName(fullpath);
|
||||
|
||||
if(!fullpath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
src: fs.readFileSync(fullpath, 'utf-8'),
|
||||
src: '{% do %}template = template || {}; template.stack = template.stack || []; template.stack.push(template.self); template.self = ' + JSON.stringify(templateName) + '{% enddo %}\n' +
|
||||
fs.readFileSync(fullpath, 'utf-8') +
|
||||
'\n{% do %}template.self = template.stack.pop();{% enddo %}',
|
||||
path: fullpath,
|
||||
noCache: true
|
||||
};
|
||||
},
|
||||
|
||||
// We handle absolute paths ourselves in ".resolve"
|
||||
/*
|
||||
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
|
||||
*/
|
||||
getSearchPath: function(filepath) {
|
||||
return _.chain(this.searchPaths)
|
||||
.sortBy(function(s) {
|
||||
return -s.length;
|
||||
})
|
||||
.find(function(basePath) {
|
||||
return (filepath && filepath.indexOf(basePath) === 0);
|
||||
})
|
||||
.value();
|
||||
},
|
||||
|
||||
/*
|
||||
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: function(from, to) {
|
||||
var searchPaths = this.searchPaths;
|
||||
|
||||
@@ -46,15 +96,8 @@ var Loader = nunjucks.Loader.extend({
|
||||
}
|
||||
|
||||
// Determine in which search folder we currently are
|
||||
var originalSearchPath = _.chain(this.searchPaths)
|
||||
.sortBy(function(s) {
|
||||
return -s.length;
|
||||
})
|
||||
.find(function(basePath) {
|
||||
return (from && from.indexOf(basePath) === 0);
|
||||
})
|
||||
.value();
|
||||
var originalFilename = originalSearchPath? path.relative(originalSearchPath, from) : null;
|
||||
var originalSearchPath = this.getSearchPath(from);
|
||||
var originalFilename = this.getTemplateName(from);
|
||||
|
||||
// If we are including same file from a different search path
|
||||
// Slice the search paths to avoid including from previous ones
|
||||
@@ -77,4 +120,4 @@ var Loader = nunjucks.Loader.extend({
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Loader;
|
||||
module.exports = ThemeLoader;
|
||||
+12
-2
@@ -184,8 +184,18 @@ HTMLPipeline.prototype.applyAnnotations = function() {
|
||||
// Extract page description from html
|
||||
// This can totally be improved
|
||||
HTMLPipeline.prototype.extractDescription = function() {
|
||||
var $p = this.$('p').first();
|
||||
var description = $p.text().trim().slice(0, 155);
|
||||
var $ = this.$;
|
||||
var $p = $('p').first();
|
||||
var $next = $p.nextUntil('h1,h2,h3,h4,h5,h6,pre,blockquote,ul,ol,div');
|
||||
|
||||
var description = $p.text().trim();
|
||||
|
||||
$next.each(function() {
|
||||
description += ' ' + $(this).text().trim();
|
||||
});
|
||||
|
||||
// Truncate description
|
||||
description = _.trunc(description, 300);
|
||||
|
||||
this.opts.onDescription(description);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,6 @@ var error = require('../utils/error');
|
||||
var pathUtil = require('../utils/path');
|
||||
var location = require('../utils/location');
|
||||
var parsers = require('../parsers');
|
||||
var gitbook = require('../gitbook');
|
||||
var pluginCompatibility = require('../plugins/compatibility');
|
||||
var HTMLPipeline = require('./html');
|
||||
|
||||
|
||||
@@ -12,27 +12,43 @@ function pluginCtx(plugin) {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// Call a function "fn" with a context of page similar to the one in GitBook v2
|
||||
/*
|
||||
Call a function "fn" with a context of page similar to the one in GitBook v2
|
||||
|
||||
@params {Page}
|
||||
@returns {String|undefined} new content of the page
|
||||
*/
|
||||
function pageHook(page, fn) {
|
||||
var ctx = {
|
||||
type: page.type,
|
||||
content: page.content,
|
||||
path: page.path,
|
||||
rawPath: page.rawPath
|
||||
};
|
||||
// Get page context
|
||||
var ctx = page.getContext().page;
|
||||
|
||||
// Add other informations
|
||||
ctx.type = page.type;
|
||||
ctx.rawPath = page.rawPath;
|
||||
ctx.path = page.path;
|
||||
|
||||
// Deprecate sections
|
||||
error.deprecateField(ctx, 'sections', [
|
||||
{ content: ctx.content }
|
||||
{ content: ctx.content, type: 'normal' }
|
||||
], '"sections" property is deprecated, use page.content instead');
|
||||
|
||||
// Keep reference of original content for compatibility
|
||||
var originalContent = ctx.content;
|
||||
|
||||
return fn(ctx)
|
||||
.then(function(result) {
|
||||
// No returned value
|
||||
// Existing content will be used
|
||||
if (!result) return undefined;
|
||||
if (result.content) {
|
||||
|
||||
// GitBook 3
|
||||
// Use returned page.content if different from original content
|
||||
if (result.content != originalContent) {
|
||||
return result.content;
|
||||
}
|
||||
|
||||
// GitBook 2 compatibility
|
||||
// Finally, use page.sections
|
||||
if (result.sections) {
|
||||
return _.pluck(result.sections, 'content').join('\n');
|
||||
}
|
||||
|
||||
@@ -144,10 +144,16 @@ function listInstalled(folder) {
|
||||
function listPlugins(book) {
|
||||
return Promise.all([
|
||||
listInstalled(path.resolve(__dirname, '../..')),
|
||||
listInstalled(book.root)
|
||||
listInstalled(book.root),
|
||||
book.isLanguageBook()? listInstalled(book.parent.root) : Promise([])
|
||||
])
|
||||
.spread(function(defaultPlugins, plugins) {
|
||||
var results = plugins.concat(defaultPlugins);
|
||||
.spread(function() {
|
||||
var args = _.toArray(arguments);
|
||||
|
||||
var results = _.reduce(args, function(out, a) {
|
||||
return out.concat(a);
|
||||
}, []);
|
||||
|
||||
return _.uniq(results, 'name');
|
||||
});
|
||||
}
|
||||
|
||||
+79
-57
@@ -10,6 +10,8 @@ var defaultBlocks = require('./blocks');
|
||||
var defaultFilters = require('./filters');
|
||||
var Loader = require('./loader');
|
||||
|
||||
var NODE_ENDARGS = '%%endargs%%';
|
||||
|
||||
// Return extension name for a specific block
|
||||
function blockExtName(name) {
|
||||
return 'Block'+name+'Extension';
|
||||
@@ -21,6 +23,12 @@ function normBlockResult(blk) {
|
||||
return blk;
|
||||
}
|
||||
|
||||
// Extract kwargs from an arguments array
|
||||
function extractKwargs(args) {
|
||||
var last = _.last(args);
|
||||
return (_.isObject(last) && last.__keywords)? args.pop() : {};
|
||||
}
|
||||
|
||||
function TemplateEngine(output) {
|
||||
this.output = output;
|
||||
this.book = output.book;
|
||||
@@ -63,6 +71,14 @@ function TemplateEngine(output) {
|
||||
// Add default blocks and filters
|
||||
this.addBlocks(defaultBlocks);
|
||||
this.addFilters(defaultFilters);
|
||||
|
||||
// Build context for this book with depreacted fields
|
||||
this.ctx = {
|
||||
template: this,
|
||||
book: this.book,
|
||||
output: this.output
|
||||
};
|
||||
error.deprecateField(this.ctx, 'generator', this.output.name, '"generator" property is deprecated, use "output.generator" instead');
|
||||
}
|
||||
|
||||
// Bind a function to a context
|
||||
@@ -71,12 +87,9 @@ TemplateEngine.prototype.bindContext = function(func) {
|
||||
var that = this;
|
||||
|
||||
return function() {
|
||||
var ctx = {
|
||||
ctx: this.ctx,
|
||||
book: that.book,
|
||||
output: that.output
|
||||
};
|
||||
error.deprecateField(ctx, 'generator', that.output.name, '"generator" property is deprecated, use "output.generator" instead');
|
||||
var ctx = _.extend({
|
||||
ctx: this.ctx
|
||||
}, that.ctx);
|
||||
|
||||
return func.apply(ctx, arguments);
|
||||
};
|
||||
@@ -174,91 +187,100 @@ TemplateEngine.prototype.addBlock = function(name, block) {
|
||||
this.tags = [name];
|
||||
|
||||
this.parse = function(parser, nodes) {
|
||||
var body = null;
|
||||
var lastBlockName = null;
|
||||
var lastBlockArgs = null;
|
||||
var allBlocks = block.blocks.concat([block.end]);
|
||||
var subbodies = {};
|
||||
|
||||
// Parse first block
|
||||
var tok = parser.nextToken();
|
||||
var args = parser.parseSignature(null, true);
|
||||
lastBlockArgs = parser.parseSignature(null, true);
|
||||
parser.advanceAfterBlockEnd(tok.value);
|
||||
|
||||
var args = new nodes.NodeList();
|
||||
var bodies = [];
|
||||
var blockNamesNode = new nodes.Array(tok.lineno, tok.colno);
|
||||
var blockArgCounts = new nodes.Array(tok.lineno, tok.colno);
|
||||
|
||||
// Parse while we found "end<block>"
|
||||
do {
|
||||
// Read body
|
||||
var currentBody = parser.parseUntilBlocks.apply(parser, allBlocks);
|
||||
|
||||
// Handle body with previous block name and args
|
||||
if (lastBlockName) {
|
||||
subbodies[lastBlockName] = subbodies[lastBlockName] || [];
|
||||
subbodies[lastBlockName].push({
|
||||
body: currentBody,
|
||||
args: lastBlockArgs
|
||||
});
|
||||
} else {
|
||||
body = currentBody;
|
||||
}
|
||||
blockNamesNode.addChild(new nodes.Literal(args.lineno, args.colno, lastBlockName));
|
||||
blockArgCounts.addChild(new nodes.Literal(args.lineno, args.colno, lastBlockArgs.children.length));
|
||||
bodies.push(currentBody);
|
||||
|
||||
// Append arguments of this block as arguments of the run function
|
||||
_.each(lastBlockArgs.children, function(child) {
|
||||
args.addChild(child);
|
||||
});
|
||||
|
||||
// Read new block
|
||||
lastBlockName = parser.peekToken().value;
|
||||
lastBlockName = parser.nextToken().value;
|
||||
|
||||
// Parse signature and move to the end of the block
|
||||
if (lastBlockName != block.end) {
|
||||
lastBlockArgs = parser.parseSignature(null, true);
|
||||
parser.advanceAfterBlockEnd(lastBlockName);
|
||||
}
|
||||
|
||||
parser.advanceAfterBlockEnd(lastBlockName);
|
||||
} while (lastBlockName != block.end);
|
||||
|
||||
parser.advanceAfterBlockEnd();
|
||||
|
||||
var bodies = [body];
|
||||
_.each(block.blocks, function(blockName) {
|
||||
subbodies[blockName] = subbodies[blockName] || [];
|
||||
if (subbodies[blockName].length === 0) {
|
||||
subbodies[blockName].push({
|
||||
args: new nodes.NodeList(),
|
||||
body: new nodes.NodeList()
|
||||
});
|
||||
}
|
||||
|
||||
bodies.push(subbodies[blockName][0].body);
|
||||
});
|
||||
args.addChild(blockNamesNode);
|
||||
args.addChild(blockArgCounts);
|
||||
args.addChild(new nodes.Literal(args.lineno, args.colno, NODE_ENDARGS));
|
||||
|
||||
return new nodes.CallExtensionAsync(this, 'run', args, bodies);
|
||||
};
|
||||
|
||||
this.run = function(context) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
var callback = args.pop();
|
||||
var fnArgs = Array.prototype.slice.call(arguments, 1);
|
||||
|
||||
// Extract blocks
|
||||
var blocks = args
|
||||
.concat([])
|
||||
.slice(-block.blocks.length);
|
||||
var args;
|
||||
var blocks = [];
|
||||
var bodies = [];
|
||||
var blockNames;
|
||||
var blockArgCounts;
|
||||
var callback;
|
||||
|
||||
// Eliminate blocks from list
|
||||
if (block.blocks.length > 0) args = args.slice(0, -block.blocks.length);
|
||||
// Extract callback
|
||||
callback = fnArgs.pop();
|
||||
|
||||
// Extract main body and kwargs
|
||||
var body = args.pop();
|
||||
var kwargs = _.isObject(_.last(args))? args.pop() : {};
|
||||
// Detect end of arguments
|
||||
var endArgIndex = fnArgs.indexOf(NODE_ENDARGS);
|
||||
|
||||
// Extract blocks body
|
||||
var _blocks = _.map(block.blocks, function(blockName, i){
|
||||
return {
|
||||
name: blockName,
|
||||
body: blocks[i]()
|
||||
};
|
||||
// Extract arguments and bodies
|
||||
args = fnArgs.slice(0, endArgIndex);
|
||||
bodies = fnArgs.slice(endArgIndex + 1);
|
||||
|
||||
// Extract block counts
|
||||
blockArgCounts = args.pop();
|
||||
blockNames = args.pop();
|
||||
|
||||
// Recreate list of blocks
|
||||
_.each(blockNames, function(name, i) {
|
||||
var countArgs = blockArgCounts[i];
|
||||
var blockBody = bodies.shift();
|
||||
|
||||
var blockArgs = countArgs > 0? args.slice(0, countArgs) : [];
|
||||
args = args.slice(countArgs);
|
||||
var blockKwargs = extractKwargs(blockArgs);
|
||||
|
||||
blocks.push({
|
||||
name: name,
|
||||
body: blockBody(),
|
||||
args: blockArgs,
|
||||
kwargs: blockKwargs
|
||||
});
|
||||
});
|
||||
|
||||
var mainBlock = blocks.shift();
|
||||
mainBlock.blocks = blocks;
|
||||
|
||||
Promise()
|
||||
.then(function() {
|
||||
return that.applyBlock(name, {
|
||||
body: body(),
|
||||
args: args,
|
||||
kwargs: kwargs,
|
||||
blocks: _blocks
|
||||
}, context);
|
||||
return that.applyBlock(name, mainBlock, context);
|
||||
})
|
||||
|
||||
// Process the block returned
|
||||
|
||||
+8
-7
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "3.0.0-pre.6",
|
||||
"version": "3.0.0-pre.9",
|
||||
"homepage": "https://www.gitbook.com",
|
||||
"description": "Library and cmd utility to generate GitBooks",
|
||||
"main": "lib/index.js",
|
||||
@@ -21,16 +21,17 @@
|
||||
"front-matter": "2.0.7",
|
||||
"gitbook-asciidoc": "1.1.0",
|
||||
"gitbook-markdown": "1.2.0",
|
||||
"gitbook-plugin-fontsettings": "1.0.2",
|
||||
"gitbook-plugin-fontsettings": "1.0.3",
|
||||
"gitbook-plugin-highlight": "2.0.2",
|
||||
"gitbook-plugin-livereload": "0.0.1",
|
||||
"gitbook-plugin-search": "1.2.0",
|
||||
"gitbook-plugin-sharing": "1.0.1",
|
||||
"gitbook-plugin-theme-default": "1.0.0-pre.5",
|
||||
"gitbook-plugin-search": "2.1.0",
|
||||
"gitbook-plugin-lunr": "1.0.0",
|
||||
"gitbook-plugin-theme-default": "1.0.0-pre.8",
|
||||
"gitbook-plugin-sharing": "1.0.2",
|
||||
"github-slugid": "1.0.1",
|
||||
"graceful-fs": "4.1.3",
|
||||
"i18n-t": "1.0.0",
|
||||
"ignore": "3.0.14",
|
||||
"ignore": "3.1.1",
|
||||
"json-schema-defaults": "0.1.1",
|
||||
"jsonschema": "1.1.0",
|
||||
"juice": "1.10.0",
|
||||
@@ -41,7 +42,7 @@
|
||||
"npm": "3.8.6",
|
||||
"npmi": "1.0.1",
|
||||
"nunjucks": "2.4.1",
|
||||
"nunjucks-autoescape": "1.0.1",
|
||||
"nunjucks-do": "1.0.0",
|
||||
"q": "1.4.1",
|
||||
"read-installed": "^4.0.3",
|
||||
"request": "2.70.0",
|
||||
|
||||
+43
-22
@@ -68,24 +68,8 @@ function setupDefaultBook(files, summary, opts) {
|
||||
}), opts);
|
||||
}
|
||||
|
||||
// Output a book with a specific generator
|
||||
function outputDefaultBook(Output, files, summary, opts) {
|
||||
return setupDefaultBook(files, summary, opts)
|
||||
.then(function(book) {
|
||||
// Parse the book
|
||||
return book.parse()
|
||||
|
||||
// Start generation
|
||||
.then(function() {
|
||||
var output = new Output(book);
|
||||
return output.generate()
|
||||
.thenResolve(output);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Output a book with a specific generator
|
||||
function outputBook(Output, files, opts) {
|
||||
// Prepare output for a book
|
||||
function setupOutput(Output, files, opts) {
|
||||
return setupBook(files, opts)
|
||||
.then(function(book) {
|
||||
// Parse the book
|
||||
@@ -93,13 +77,43 @@ function outputBook(Output, files, opts) {
|
||||
|
||||
// Start generation
|
||||
.then(function() {
|
||||
var output = new Output(book);
|
||||
return output.generate()
|
||||
.thenResolve(output);
|
||||
return new Output(book);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Prepare output for a book
|
||||
function setupDefaultOutput(Output, files, summary, opts) {
|
||||
return setupDefaultBook(files, summary, opts)
|
||||
.then(function(book) {
|
||||
// Parse the book
|
||||
return book.parse()
|
||||
|
||||
// Start generation
|
||||
.then(function() {
|
||||
return new Output(book);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Output a book with a specific generator
|
||||
function outputDefaultBook(Output, files, summary, opts) {
|
||||
return setupDefaultOutput(Output, files, summary, opts)
|
||||
.then(function(output) {
|
||||
return output.generate()
|
||||
.thenResolve(output);
|
||||
});
|
||||
}
|
||||
|
||||
// Output a book with a specific generator
|
||||
function outputBook(Output, files, opts) {
|
||||
return setupOutput(Output, files, opts)
|
||||
.then(function(output) {
|
||||
return output.generate()
|
||||
.thenResolve(output);
|
||||
});
|
||||
}
|
||||
|
||||
// Log an error
|
||||
function logError(err) {
|
||||
console.log(err.stack || err);
|
||||
@@ -107,10 +121,17 @@ function logError(err) {
|
||||
|
||||
module.exports = {
|
||||
fs: nodeFS,
|
||||
|
||||
setupFS: setupFS,
|
||||
|
||||
setupBook: setupBook,
|
||||
outputBook: outputBook,
|
||||
setupDefaultBook: setupDefaultBook,
|
||||
|
||||
setupOutput: setupOutput,
|
||||
setupDefaultOutput: setupDefaultOutput,
|
||||
|
||||
outputBook: outputBook,
|
||||
outputDefaultBook: outputDefaultBook,
|
||||
|
||||
logError: logError
|
||||
};
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "gitbook-plugin-dep1",
|
||||
"version": "1.0.0",
|
||||
"engines": {
|
||||
"gitbook": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"gitbook-plugin-dep2": "*"
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "gitbook-plugin-dep2",
|
||||
"version": "1.0.0",
|
||||
"engines": {
|
||||
"gitbook": "*"
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
hooks: {
|
||||
page: function(page) {
|
||||
page.sections[0].content = 'Hello (sections) ' + page.content;
|
||||
return page;
|
||||
}
|
||||
}
|
||||
};
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "gitbook-plugin-test-deprecated",
|
||||
"version": "1.0.0",
|
||||
"engines": {
|
||||
"gitbook": "*"
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -1,5 +1,3 @@
|
||||
var should = require('should');
|
||||
|
||||
module.exports = {
|
||||
hooks: {
|
||||
'init': function() {
|
||||
@@ -11,6 +9,10 @@ module.exports = {
|
||||
},
|
||||
'finish:before': function() {
|
||||
global._hooks.push('finish:before');
|
||||
},
|
||||
page: function(page) {
|
||||
page.content = 'Hello ' + page.content;
|
||||
return page;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+2
-1
@@ -53,9 +53,10 @@ describe('JSON Output', function() {
|
||||
|
||||
it('should correctly generate a README.json for the whole book', function() {
|
||||
output.should.have.file('README.json');
|
||||
|
||||
var json = require(output.resolve('README.json'));
|
||||
|
||||
json.book.language.should.equal('en');
|
||||
|
||||
json.should.have.property('languages')
|
||||
.with.property('list').with.lengthOf(2);
|
||||
});
|
||||
|
||||
@@ -20,7 +20,8 @@ describe('Website Output', function() {
|
||||
});
|
||||
|
||||
it('should correctly copy assets', function() {
|
||||
output.should.have.file('gitbook/app.js');
|
||||
output.should.have.file('gitbook/gitbook.js');
|
||||
output.should.have.file('gitbook/theme.js');
|
||||
output.should.have.file('gitbook/images/favicon.ico');
|
||||
});
|
||||
|
||||
@@ -84,12 +85,16 @@ describe('Website Output', function() {
|
||||
});
|
||||
|
||||
it('should correctly copy assets', function() {
|
||||
output.should.have.file('gitbook/app.js');
|
||||
output.should.have.file('gitbook/gitbook.js');
|
||||
output.should.have.file('gitbook/theme.js');
|
||||
});
|
||||
|
||||
it('should not copy assets for each language', function() {
|
||||
output.should.have.not.file('en/gitbook/app.js');
|
||||
output.should.have.not.file('fr/gitbook/app.js');
|
||||
output.should.have.not.file('en/gitbook/gitbook.js');
|
||||
output.should.have.not.file('fr/gitbook/gitbook.js');
|
||||
|
||||
output.should.have.not.file('en/gitbook/theme.js');
|
||||
output.should.have.not.file('fr/gitbook/theme.js');
|
||||
});
|
||||
|
||||
it('should correctly generate an index.html', function() {
|
||||
|
||||
+3
-3
@@ -8,7 +8,7 @@ describe('Page', function() {
|
||||
return mock.setupDefaultBook({
|
||||
'README.md': ' # Hello World\n\nThis is a description',
|
||||
'heading.md': '# Hello\n\n## World',
|
||||
'description.md': '# This is a title\n\nThis is the short description.\n\nNot this one.',
|
||||
'description.md': '# This is a title\n\nThis is the short description.\n\nAnd the rest of the description.\n\n# Heading\n\nThis is not in the description',
|
||||
'frontmatter/description.md': '---\ndescription: Hello World\n---\n\n# This is a title\n\nThis is not the description',
|
||||
'frontmatter/var.md': '---\ntest: Hello World\n---\n\n{{ page.test }}',
|
||||
|
||||
@@ -141,7 +141,7 @@ describe('Page', function() {
|
||||
|
||||
return page.toHTML(output)
|
||||
.then(function() {
|
||||
page.attributes.description.should.equal('This is the short description.');
|
||||
page.attributes.description.should.equal('This is the short description. And the rest of the description.');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -289,7 +289,7 @@ describe('Page', function() {
|
||||
|
||||
it('should keep external images path', function() {
|
||||
page.content.should.be.html({
|
||||
'img[src="https:/upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"]': {
|
||||
'img[src="https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"]': {
|
||||
count: 1
|
||||
}
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ var registry = require('../lib/plugins/registry');
|
||||
var Output = require('../lib/output/base');
|
||||
var PluginsManager = require('../lib/plugins');
|
||||
var BookPlugin = require('../lib/plugins/plugin');
|
||||
var JSONOutput = require('../lib/output/json');
|
||||
|
||||
var PLUGINS_ROOT = path.resolve(__dirname, 'node_modules');
|
||||
|
||||
@@ -229,6 +230,42 @@ describe('Plugins', function() {
|
||||
global._hooks.should.deepEqual(['init']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('Hook "page"', function() {
|
||||
var pluginDeprecated;
|
||||
|
||||
before(function() {
|
||||
pluginDeprecated = TestPlugin(book, 'test-deprecated');
|
||||
return pluginDeprecated.load(PLUGINS_ROOT);
|
||||
});
|
||||
|
||||
it('should update content using "content" property', function() {
|
||||
return mock.setupDefaultOutput(JSONOutput)
|
||||
.then(function(output) {
|
||||
output.plugins.load(plugin);
|
||||
|
||||
return output.generate()
|
||||
.then(function() {
|
||||
var json = require(output.resolve('README.json'));
|
||||
json.page.content.should.equal('Hello <p>Hello</p>\n');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should update content using deprecated "sections" property', function() {
|
||||
return mock.setupDefaultOutput(JSONOutput)
|
||||
.then(function(output) {
|
||||
output.plugins.load(pluginDeprecated);
|
||||
|
||||
return output.generate()
|
||||
.then(function() {
|
||||
var json = require(output.resolve('README.json'));
|
||||
json.page.content.should.equal('Hello (sections) <p>Hello</p>\n');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user