diff options
author | Samy Pessé <samypesse@gmail.com> | 2016-02-15 14:07:26 +0100 |
---|---|---|
committer | Samy Pessé <samypesse@gmail.com> | 2016-02-15 14:07:26 +0100 |
commit | 354fe1274242bef87b739dbe43cdb634ea573662 (patch) | |
tree | d4a7c1f48878ba4eb6793535cc42a516a143bc0e | |
parent | 1b19c6660303cb6ce98ac0ad76580f66b52985f1 (diff) | |
download | gitbook-354fe1274242bef87b739dbe43cdb634ea573662.zip gitbook-354fe1274242bef87b739dbe43cdb634ea573662.tar.gz gitbook-354fe1274242bef87b739dbe43cdb634ea573662.tar.bz2 |
Fix addBlock for template engine
Add test for code block
-rw-r--r-- | lib/book.js | 2 | ||||
-rw-r--r-- | lib/output/ebook.js | 2 | ||||
-rw-r--r-- | lib/output/website/index.js | 2 | ||||
-rw-r--r-- | lib/page/index.js | 8 | ||||
-rw-r--r-- | lib/template/index.js | 83 | ||||
-rw-r--r-- | lib/utils/error.js | 18 | ||||
-rw-r--r-- | lib/utils/logger.js (renamed from lib/logger.js) | 2 | ||||
-rw-r--r-- | package.json | 5 | ||||
-rw-r--r-- | test/page.js | 34 | ||||
-rw-r--r-- | test/template.js | 24 |
10 files changed, 168 insertions, 12 deletions
diff --git a/lib/book.js b/lib/book.js index cca1da5..173e9cc 100644 --- a/lib/book.js +++ b/lib/book.js @@ -3,7 +3,6 @@ var path = require('path'); var Ignore = require('ignore'); var parsers = require('gitbook-parsers'); -var Logger = require('./logger'); var Config = require('./config'); var Readme = require('./backbone/readme'); var Glossary = require('./backbone/glossary'); @@ -13,6 +12,7 @@ var Page = require('./page'); var pathUtil = require('./utils/path'); var error = require('./utils/error'); var Promise = require('./utils/promise'); +var Logger = require('./utils/logger'); /* diff --git a/lib/output/ebook.js b/lib/output/ebook.js index b968006..b4cd550 100644 --- a/lib/output/ebook.js +++ b/lib/output/ebook.js @@ -10,5 +10,7 @@ function EbookOutput() { util.inherits(EbookOutput, AssetsInliner); util.inherits(EbookOutput, WebsiteOutput); +EbookOutput.prototype.name = 'ebook'; + module.exports = EbookOutput; diff --git a/lib/output/website/index.js b/lib/output/website/index.js index a35cafe..b268eec 100644 --- a/lib/output/website/index.js +++ b/lib/output/website/index.js @@ -8,6 +8,8 @@ function WebsiteOutput() { } util.inherits(WebsiteOutput, FolderOutput); +WebsiteOutput.prototype.name = 'ebook'; + // Write a page (parsable file) WebsiteOutput.prototype.onPage = function(page) { diff --git a/lib/page/index.js b/lib/page/index.js index b3ff00f..342830a 100644 --- a/lib/page/index.js +++ b/lib/page/index.js @@ -139,7 +139,7 @@ Page.prototype.toHTML = function(output) { // Render template .then(function() { - return output.template.renderString(that.content, that.getContext(), { + return output.template.render(that.content, that.getContext(), { file: that.path }) .then(that.update); @@ -154,6 +154,12 @@ Page.prototype.toHTML = function(output) { }); }) + // Post process templating + .then(function() { + return output.template.postProcess(that.content) + .then(that.update); + }) + // Normalize HTML output .then(function() { var pipelineOpts = { diff --git a/lib/template/index.js b/lib/template/index.js index 3f74267..8266572 100644 --- a/lib/template/index.js +++ b/lib/template/index.js @@ -64,6 +64,20 @@ function TemplateEngine(output) { this.addBlocks(defaultBlocks); } +// Bind a function to a context +// Filters and blocks are binded to this context +TemplateEngine.prototype.bindContext = function(func) { + var ctx = { + ctx: this.ctx, + book: this.book, + output: this.output + }; + + error.deprecateField(ctx, 'generator', this.output.name, 'generator property is deprecated, use output.name instead'); + + return _.bind(func, ctx); +}; + // Interpolate a string content to replace shortcuts according to the filetype TemplateEngine.prototype.interpolate = function(filepath, source) { var parser = parsers.get(path.extname(filepath)); @@ -240,7 +254,7 @@ TemplateEngine.prototype.addBlock = function(name, block) { }, context); }) - // process the block returned + // Process the block returned .then(that.processBlock) .nodeify(callback); }; @@ -303,9 +317,30 @@ TemplateEngine.prototype.applyBlock = function(name, blk, ctx) { else return normBlockResult(r); }; +// Process the result of block in a context +TemplateEngine.prototype.processBlock = function(blk) { + blk = _.defaults(blk, { + parse: false, + post: undefined + }); + blk.id = _.uniqueId('blk'); -// Render a string -TemplateEngine.prototype.renderString = function(content, context, options) { + var toAdd = (!blk.parse) || (blk.post !== undefined); + + // Add to global map + if (toAdd) this.blockBodies[blk.id] = blk; + + // Parsable block, just return it + if (blk.parse) { + return blk.body; + } + + // Return it as a position marker + return '@%@'+blk.id+'@%@'; +}; + +// Render a string (without post processing) +TemplateEngine.prototype.render = function(content, context, options) { options = _.defaults(options || {}, { path: null }); @@ -345,6 +380,12 @@ TemplateEngine.prototype.renderString = function(content, context, options) { }); }; +// Render a string with post-processing +TemplateEngine.prototype.renderString = function(content, context, options) { + return this.render(content, context, options) + .then(this.postProcess); +}; + // Apply a shortcut to a string TemplateEngine.prototype.applyShortcut = function(content, shortcut) { var regex = new RegExp( @@ -356,6 +397,21 @@ TemplateEngine.prototype.applyShortcut = function(content, shortcut) { }); }; +// Replace position markers of blocks by body after processing +// This is done to avoid that markdown/asciidoc processer parse the block content +TemplateEngine.prototype.replaceBlocks = function(content) { + var that = this; + + return content.replace(/\@\%\@([\s\S]+?)\@\%\@/g, function(match, key) { + var blk = that.blockBodies[key]; + if (!blk) return match; + + var body = blk.body; + + return body; + }); +}; + // Apply all shortcuts to a template TemplateEngine.prototype.applyShortcuts = function(type, content) { return _.chain(this.shortcuts) @@ -367,4 +423,25 @@ TemplateEngine.prototype.applyShortcuts = function(type, content) { }; +// Post process content +TemplateEngine.prototype.postProcess = function(content) { + var that = this; + + return Promise(content) + .then(that.replaceBlocks) + .then(function(_content) { + return Promise.serie(that.blockBodies, function(blk, blkId) { + return Promise() + .then(function() { + if (!blk.post) return; + return blk.post(); + }) + .then(function() { + delete that.blockBodies[blkId]; + }); + }) + .thenResolve(_content); + }); +}; + module.exports = TemplateEngine; diff --git a/lib/utils/error.js b/lib/utils/error.js index 883a7c6..c1abebd 100644 --- a/lib/utils/error.js +++ b/lib/utils/error.js @@ -1,6 +1,11 @@ var _ = require('lodash'); var TypedError = require('error/typed'); var WrappedError = require('error/wrapped'); +var deprecated = require('deprecated'); + +var Logger = require('./logger'); + +var log = new Logger(); // Enforce as an Error object, and cleanup message function enforce(err) { @@ -43,6 +48,14 @@ var TemplateError = WrappedError({ filename: null }); +// Deprecate methods/fields +function deprecateMethod(fn, msg) { + return deprecated.method(msg, log.warn.ln, fn); +} +function deprecateField(obj, prop, value, msg) { + return deprecated.field(msg, log.warn.ln, obj, prop, value); +} + module.exports = { enforce: enforce, @@ -52,5 +65,8 @@ module.exports = { FileNotFoundError: FileNotFoundError, FileOutOfScopeError: FileOutOfScopeError, - TemplateError: TemplateError + TemplateError: TemplateError, + + deprecateMethod: deprecateMethod, + deprecateField: deprecateField }; diff --git a/lib/logger.js b/lib/utils/logger.js index 7428180..9dda57d 100644 --- a/lib/logger.js +++ b/lib/utils/logger.js @@ -20,7 +20,7 @@ var COLORS = { function Logger(write, logLevel) { if (!(this instanceof Logger)) return new Logger(write, logLevel); - this._write = write; + this._write = write || function(msg) { process.stdout.write(msg); }; this.lastChar = '\n'; // Define log level diff --git a/package.json b/package.json index 69e91b7..3eb6001 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "gitbook-plugin-sharing": "1.0.1", "gitbook-plugin-search": "1.1.0", "gitbook-plugin-fontsettings": "1.0.2", - "nunjucks": "2.2.0", + "nunjucks": "2.3.0", "nunjucks-autoescape": "1.0.0", "nunjucks-filter": "1.0.0", "i18n": "0.5.0", @@ -43,7 +43,8 @@ "merge-defaults": "0.2.1", "github-slugid": "1.0.0", "destroy": "1.0.4", - "ignore": "2.2.19" + "ignore": "2.2.19", + "deprecated": "0.0.1" }, "devDependencies": { "eslint": "1.5.0", diff --git a/test/page.js b/test/page.js index fb3e3e5..b947519 100644 --- a/test/page.js +++ b/test/page.js @@ -8,7 +8,13 @@ describe('Page', function() { return mock.setupDefaultBook({ 'heading.md': '# Hello\n\n## World', 'links.md': '[link](hello.md) [readme](README.md)', + + 'codes/simple.md': '```hello world```', + 'codes/lang.md': '```js\nhello world\n```', + 'codes/lang.adoc': '```js\nhello world\n```', + 'folder/paths.md': '', + 'variables/file/mtime.md': '{{ file.mtime }}', 'variables/file/path.md': '{{ file.path }}', 'variables/page/title.md': '{{ page.title }}', @@ -86,6 +92,34 @@ describe('Page', function() { }); }); + describe('Code Blocks', function() { + var page; + + before(function() { + output.template.addBlock('code', function(blk) { + return (blk.kwargs.language || '') + blk.body + 'test'; + }); + }); + + it('should apply "code" block', function() { + page = book.addPage('codes/simple.md'); + return page.toHTML(output) + .should.be.fulfilledWith('<p><code>hello worldtest</code></p>\n'); + }); + + it('should add language as kwargs', function() { + page = book.addPage('codes/lang.md'); + return page.toHTML(output) + .should.be.fulfilledWith('<pre><code class="lang-js">jshello world\ntest</code></pre>\n'); + }); + + it('should add language as kwargs (asciidoc)', function() { + page = book.addPage('codes/lang.adoc'); + return page.toHTML(output) + .should.be.fulfilledWith('<div class="listingblock">\n<div class="content">\n<pre class="highlight"><code class="language-js" data-lang="js">jshello worldtest</code></pre>\n</div>\n</div>'); + }); + }); + describe('Links', function() { var page; diff --git a/test/template.js b/test/template.js index 536043d..d12a641 100644 --- a/test/template.js +++ b/test/template.js @@ -6,9 +6,7 @@ describe('Template', function() { var output; before(function() { - return mock.outputDefaultBook(Output, { - 'test.md': 'World' - }) + return mock.outputDefaultBook(Output, {}) .then(function(_output) { output = _output; }); @@ -25,4 +23,24 @@ describe('Template', function() { .should.be.fulfilledWith('Version is '+pkg.version); }); }); + + describe('Blocks', function() { + it('should correctly add a block', function() { + output.template.addBlock('sayhello', function(blk) { + return 'Hello ' + blk.body + '!'; + }); + + return output.template.renderString('{% sayhello %}World{% endsayhello %}') + .should.be.fulfilledWith('Hello World!'); + }); + + it('should correctly add a block with kwargs', function() { + output.template.addBlock('sayhello_kwargs', function(blk) { + return 'Hello ' + blk.kwargs.name + '!'; + }); + + return output.template.renderString('{% sayhello_kwargs name="World" %}{% endsayhello_kwargs %}') + .should.be.fulfilledWith('Hello World!'); + }); + }); }); |