diff options
-rw-r--r-- | packages/gitbook/src/models/__tests__/page.js | 5 | ||||
-rw-r--r-- | packages/gitbook/src/models/__tests__/templateBlock.js | 98 | ||||
-rw-r--r-- | packages/gitbook/src/models/__tests__/uriIndex.js | 14 | ||||
-rw-r--r-- | packages/gitbook/src/models/templateBlock.js | 410 | ||||
-rw-r--r-- | packages/gitbook/src/models/templateEngine.js | 244 | ||||
-rw-r--r-- | packages/gitbook/src/parse/__tests__/parseURIIndexFromPages.js | 15 | ||||
-rw-r--r-- | packages/gitbook/src/templating/__tests__/conrefsLoader.js | 53 |
7 files changed, 420 insertions, 419 deletions
diff --git a/packages/gitbook/src/models/__tests__/page.js b/packages/gitbook/src/models/__tests__/page.js index ad9420e..b004121 100644 --- a/packages/gitbook/src/models/__tests__/page.js +++ b/packages/gitbook/src/models/__tests__/page.js @@ -5,7 +5,7 @@ describe('Page', function() { describe('toText', function() { it('must not prepend frontmatter if no attributes', function() { - const page = Page().merge({ + const page = (new Page()).merge({ content: 'Hello World' }); @@ -13,7 +13,7 @@ describe('Page', function() { }); it('must prepend frontmatter if attributes', function() { - const page = Page().merge({ + const page = (new Page()).merge({ content: 'Hello World', attributes: Immutable.fromJS({ hello: 'world' @@ -24,4 +24,3 @@ describe('Page', function() { }); }); }); - diff --git a/packages/gitbook/src/models/__tests__/templateBlock.js b/packages/gitbook/src/models/__tests__/templateBlock.js index 20511be..408b57e 100644 --- a/packages/gitbook/src/models/__tests__/templateBlock.js +++ b/packages/gitbook/src/models/__tests__/templateBlock.js @@ -5,49 +5,56 @@ const Promise = require('../../utils/promise'); describe('TemplateBlock', function() { const TemplateBlock = require('../templateBlock'); - describe('create', function() { + describe('.create', function() { it('must initialize a simple TemplateBlock from a function', function() { const templateBlock = TemplateBlock.create('sayhello', function(block) { - return { - body: '<p>Hello, World!</p>', - parse: true - }; + return { message: 'Hello World' }; }); - // Check basic templateBlock properties expect(templateBlock.getName()).toBe('sayhello'); expect(templateBlock.getEndTag()).toBe('endsayhello'); expect(templateBlock.getBlocks().size).toBe(0); expect(templateBlock.getExtensionName()).toBe('BlocksayhelloExtension'); + }); + }); + + describe('.toProps', function() { + it('must handle sync method', function() { + const templateBlock = TemplateBlock.create('sayhello', function(block) { + return { message: 'Hello World' }; + }); - // Check result of applying block - return Promise() - .then(function() { - return templateBlock.applyBlock(); - }) - .then(function(result) { - expect(result.name).toBe('sayhello'); - expect(result.body).toBe('<p>Hello, World!</p>'); + return templateBlock.toProps() + .then(function(props) { + expect(props).toEqual({ message: 'Hello World' }); + }); + }); + + it('must not fsil if return a string', function() { + const templateBlock = TemplateBlock.create('sayhello', function(block) { + return 'Hello World'; + }); + + return templateBlock.toProps() + .then(function(props) { + expect(props).toEqual({ children: 'Hello World' }); }); }); }); - describe('getShortcuts', function() { + describe('.getShortcuts', function() { it('must return undefined if no shortcuts', function() { const templateBlock = TemplateBlock.create('sayhello', function(block) { - return { - body: '<p>Hello, World!</p>', - parse: true - }; + return { message: 'Hello World' }; }); expect(templateBlock.getShortcuts()).toNotExist(); }); - it('must return complete shortcut', function() { + it('.must return complete shortcut', function() { const templateBlock = TemplateBlock.create('sayhello', { process(block) { - return '<p>Hello, World!</p>'; + return { message: 'Hello World' }; }, shortcuts: { parsers: ['markdown'], @@ -66,43 +73,30 @@ describe('TemplateBlock', function() { }); }); - describe('toNunjucksExt()', function() { - it('should replace by block anchor', function() { + describe('.toNunjucksExt()', function() { + it('should render children correctly', function() { const templateBlock = TemplateBlock.create('sayhello', function(block) { return 'Hello'; }); - let blocks = {}; - // Create a fresh Nunjucks environment const env = new nunjucks.Environment(null, { autoescape: false }); // Add template block to environement - const Ext = templateBlock.toNunjucksExt({}, blocks); + const Ext = templateBlock.toNunjucksExt(); env.addExtension(templateBlock.getExtensionName(), new Ext()); // Render a template using the block const src = '{% sayhello %}{% endsayhello %}'; return Promise.nfcall(env.renderString.bind(env), src) .then(function(res) { - blocks = Immutable.fromJS(blocks); - expect(blocks.size).toBe(1); - - const blockId = blocks.keySeq().get(0); - const block = blocks.get(blockId); - - expect(res).toBe('{{-%' + blockId + '%-}}'); - expect(block.get('body')).toBe('Hello'); - expect(block.get('name')).toBe('sayhello'); + expect(res).toBe('<xblock name="sayhello" props="{}">Hello</xblock>'); }); }); - it('must create a valid nunjucks extension', function() { + it('must handle HTML children', function() { const templateBlock = TemplateBlock.create('sayhello', function(block) { - return { - body: '<p>Hello, World!</p>', - parse: true - }; + return '<p>Hello, World!</p>'; }); // Create a fresh Nunjucks environment @@ -116,15 +110,14 @@ describe('TemplateBlock', function() { const src = '{% sayhello %}{% endsayhello %}'; return Promise.nfcall(env.renderString.bind(env), src) .then(function(res) { - expect(res).toBe('<p>Hello, World!</p>'); + expect(res).toBe('<xblock name="sayhello" props="{}"><p>Hello, World!</p></xblock>'); }); }); - it('must apply block arguments correctly', function() { + it('must inline props without children', function() { const templateBlock = TemplateBlock.create('sayhello', function(block) { return { - body: '<' + block.kwargs.tag + '>Hello, ' + block.kwargs.name + '!</' + block.kwargs.tag + '>', - parse: true + message: block.kwargs.tag + ' ' + block.kwargs.name }; }); @@ -139,17 +132,17 @@ describe('TemplateBlock', function() { const src = '{% sayhello name="Samy", tag="p" %}{% endsayhello %}'; return Promise.nfcall(env.renderString.bind(env), src) .then(function(res) { - expect(res).toBe('<p>Hello, Samy!</p>'); + expect(res).toBe('<xblock name="sayhello" props="{"message":"p Samy"}"></xblock>'); }); }); it('must accept an async function', function() { const templateBlock = TemplateBlock.create('sayhello', function(block) { return Promise() + .delay(1) .then(function() { return { - body: 'Hello ' + block.body, - parse: true + children: 'Hello ' + block.children }; }); }); @@ -165,7 +158,7 @@ describe('TemplateBlock', function() { const src = '{% sayhello %}Samy{% endsayhello %}'; return Promise.nfcall(env.renderString.bind(env), src) .then(function(res) { - expect(res).toBe('Hello Samy'); + expect(res).toBe('<xblock name="sayhello" props="{}">Hello Samy</xblock>'); }); }); @@ -177,13 +170,10 @@ describe('TemplateBlock', function() { const nested = {}; block.blocks.forEach(function(blk) { - nested[blk.name] = blk.body.trim(); + nested[blk.name] = blk.children.trim(); }); - return { - body: '<p class="yoda">' + nested.end + ' ' + nested.start + '</p>', - parse: true - }; + return '<p class="yoda">' + nested.end + ' ' + nested.start + '</p>'; } }); @@ -198,7 +188,7 @@ describe('TemplateBlock', function() { const src = '{% yoda %}{% start %}this sentence should be{% end %}inverted{% endyoda %}'; return Promise.nfcall(env.renderString.bind(env), src) .then(function(res) { - expect(res).toBe('<p class="yoda">inverted this sentence should be</p>'); + expect(res).toBe('<xblock name="yoda" props="{}"><p class="yoda">inverted this sentence should be</p></xblock>'); }); }); }); diff --git a/packages/gitbook/src/models/__tests__/uriIndex.js b/packages/gitbook/src/models/__tests__/uriIndex.js index db3b13c..4448683 100644 --- a/packages/gitbook/src/models/__tests__/uriIndex.js +++ b/packages/gitbook/src/models/__tests__/uriIndex.js @@ -1,6 +1,6 @@ const URIIndex = require('../uriIndex'); -describe.only('URIIndex', () => { +describe('URIIndex', () => { let index; before(() => { @@ -40,6 +40,18 @@ describe.only('URIIndex', () => { }); + describe('.resolveToURL', () => { + + it('should resolve a basic file path with directory index', () => { + expect(index.resolveToURL('README.md')).toBe('./'); + }); + + it('should resolve a basic file path with directory index', () => { + expect(index.resolveToURL('hello/README.md')).toBe('hello/'); + }); + + }); + describe('.resolveFrom', () => { it('should resolve correctly in same directory', () => { diff --git a/packages/gitbook/src/models/templateBlock.js b/packages/gitbook/src/models/templateBlock.js index e8d2aae..61c006f 100644 --- a/packages/gitbook/src/models/templateBlock.js +++ b/packages/gitbook/src/models/templateBlock.js @@ -1,6 +1,6 @@ const is = require('is'); const extend = require('extend'); -const Immutable = require('immutable'); +const { Record, List, Map } = require('immutable'); const escape = require('escape-html'); const Promise = require('../utils/promise'); @@ -9,232 +9,236 @@ const TemplateShortcut = require('./templateShortcut'); const NODE_ENDARGS = '%%endargs%%'; const HTML_TAGNAME = 'xblock'; -const TemplateBlock = Immutable.Record({ +const DEFAULTS = { // Name of block, also the start tag - name: String(), - + name: String(), // End tag, default to "end<name>" - end: String(), - + end: String(), // Function to process the block content - process: Function(), - + process: Function(), // List of String, for inner block tags - blocks: Immutable.List(), - + blocks: List(), // List of shortcuts to replace with this block - shortcuts: Immutable.Map() -}, 'TemplateBlock'); - -TemplateBlock.prototype.getName = function() { - return this.get('name'); -}; - -TemplateBlock.prototype.getEndTag = function() { - return this.get('end') || ('end' + this.getName()); + shortcuts: Map() }; -TemplateBlock.prototype.getProcess = function() { - return this.get('process'); -}; - -TemplateBlock.prototype.getBlocks = function() { - return this.get('blocks'); -}; - - -/** - * Return shortcuts associated with this block or undefined - * @return {TemplateShortcut|undefined} - */ -TemplateBlock.prototype.getShortcuts = function() { - const shortcuts = this.get('shortcuts'); - if (shortcuts.size === 0) { - return undefined; +class TemplateBlock extends Record(DEFAULTS) { + getName() { + return this.get('name'); } - return TemplateShortcut.createForBlock(this, shortcuts); -}; - -/** - * Return name for the nunjucks extension - * @return {String} - */ -TemplateBlock.prototype.getExtensionName = function() { - return 'Block' + this.getName() + 'Extension'; -}; - -/** - * Return a nunjucks extension to represents this block - * @return {Nunjucks.Extension} - */ -TemplateBlock.prototype.toNunjucksExt = function(mainContext, blocksOutput) { - blocksOutput = blocksOutput || {}; - - const that = this; - const name = this.getName(); - const endTag = this.getEndTag(); - const blocks = this.getBlocks().toJS(); - - function Ext() { - this.tags = [name]; - - this.parse = function(parser, nodes) { - let lastBlockName = null; - let lastBlockArgs = null; - const allBlocks = blocks.concat([endTag]); - - // Parse first block - const tok = parser.nextToken(); - lastBlockArgs = parser.parseSignature(null, true); - parser.advanceAfterBlockEnd(tok.value); - - const args = new nodes.NodeList(); - const bodies = []; - const blockNamesNode = new nodes.Array(tok.lineno, tok.colno); - const blockArgCounts = new nodes.Array(tok.lineno, tok.colno); - - // Parse while we found "end<block>" - do { - // Read body - const currentBody = parser.parseUntilBlocks(...allBlocks); - - // Handle body with previous block name and args - 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 - lastBlockArgs.children.forEach(function(child) { - args.addChild(child); - }); - - // Read new block - lastBlockName = parser.nextToken().value; - - // Parse signature and move to the end of the block - if (lastBlockName != endTag) { - lastBlockArgs = parser.parseSignature(null, true); - } - - parser.advanceAfterBlockEnd(lastBlockName); - } while (lastBlockName != endTag); - - 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, ...fnArgs) { - let args; - const blocks = []; - let bodies = []; + getEndTag() { + return this.get('end') || ('end' + this.getName()); + } - // Extract callback - const callback = fnArgs.pop(); + getProcess() { + return this.get('process'); + } - // Detect end of arguments - const endArgIndex = fnArgs.indexOf(NODE_ENDARGS); + getBlocks() { + return this.get('blocks'); + } - // Extract arguments and bodies - args = fnArgs.slice(0, endArgIndex); - bodies = fnArgs.slice(endArgIndex + 1); - // Extract block counts - const blockArgCounts = args.pop(); - const blockNames = args.pop(); + /** + * Return shortcuts associated with this block or undefined + * @return {TemplateShortcut|undefined} + */ + getShortcuts() { + const shortcuts = this.get('shortcuts'); + if (shortcuts.size === 0) { + return undefined; + } - // Recreate list of blocks - blockNames.forEach((blkName, i) => { - const countArgs = blockArgCounts[i]; - const blockBody = bodies.shift(); + return TemplateShortcut.createForBlock(this, shortcuts); + } - const blockArgs = countArgs > 0 ? args.slice(0, countArgs) : []; - args = args.slice(countArgs); - const blockKwargs = extractKwargs(blockArgs); + /** + * Return name for the nunjucks extension + * @return {String} + */ + getExtensionName() { + return 'Block' + this.getName() + 'Extension'; + } - blocks.push({ - name: blkName, - children: blockBody(), - args: blockArgs, - kwargs: blockKwargs + /** + * Return a nunjucks extension to represents this block + * @return {Nunjucks.Extension} + */ + toNunjucksExt(mainContext = {}) { + const that = this; + const name = this.getName(); + const endTag = this.getEndTag(); + const blocks = this.getBlocks().toJS(); + + function Ext() { + this.tags = [name]; + + this.parse = (parser, nodes) => { + let lastBlockName = null; + let lastBlockArgs = null; + const allBlocks = blocks.concat([endTag]); + + // Parse first block + const tok = parser.nextToken(); + lastBlockArgs = parser.parseSignature(null, true); + parser.advanceAfterBlockEnd(tok.value); + + const args = new nodes.NodeList(); + const bodies = []; + const blockNamesNode = new nodes.Array(tok.lineno, tok.colno); + const blockArgCounts = new nodes.Array(tok.lineno, tok.colno); + + // Parse while we found "end<block>" + do { + // Read body + const currentBody = parser.parseUntilBlocks(...allBlocks); + + // Handle body with previous block name and args + 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 + lastBlockArgs.children.forEach(function(child) { + args.addChild(child); + }); + + // Read new block + lastBlockName = parser.nextToken().value; + + // Parse signature and move to the end of the block + if (lastBlockName != endTag) { + lastBlockArgs = parser.parseSignature(null, true); + } + + parser.advanceAfterBlockEnd(lastBlockName); + } while (lastBlockName != endTag); + + 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 = (context, ...fnArgs) => { + let args; + const blocks = []; + let bodies = []; + + // Extract callback + const callback = fnArgs.pop(); + + // Detect end of arguments + const endArgIndex = fnArgs.indexOf(NODE_ENDARGS); + + // Extract arguments and bodies + args = fnArgs.slice(0, endArgIndex); + bodies = fnArgs.slice(endArgIndex + 1); + + // Extract block counts + const blockArgCounts = args.pop(); + const blockNames = args.pop(); + + // Recreate list of blocks + blockNames.forEach((blkName, i) => { + const countArgs = blockArgCounts[i]; + const blockBody = bodies.shift(); + + const blockArgs = countArgs > 0 ? args.slice(0, countArgs) : []; + args = args.slice(countArgs); + const blockKwargs = extractKwargs(blockArgs); + + blocks.push({ + name: blkName, + children: blockBody(), + args: blockArgs, + kwargs: blockKwargs + }); }); - }); - const mainBlock = blocks.shift(); - mainBlock.blocks = blocks; - - Promise() - .then(function() { - const ctx = extend({ - ctx: context - }, mainContext || {}); - - return that.applyBlock(mainBlock, ctx); - }) - .then(function(props) { - return that.blockResultToHtml(props); - }) - .nodeify(callback); - }; + const mainBlock = blocks.shift(); + mainBlock.blocks = blocks; + + Promise() + .then(function() { + const ctx = extend({ + ctx: context + }, mainContext); + + return that.toProps(mainBlock, ctx); + }) + .then(function(props) { + return that.toHTML(props); + }) + .nodeify(callback); + }; + } + + return Ext; } - return Ext; -}; - -/** - * Apply a block to a content. - * - * @param {Object} inner - * @param {Object} context - * @return {Promise<Props>|Props} - */ -TemplateBlock.prototype.applyBlock = function(inner, context) { - const processFn = this.getProcess(); - - inner = inner || {}; - inner.args = inner.args || []; - inner.kwargs = inner.kwargs || {}; - inner.blocks = inner.blocks || []; - - return processFn.call(context, inner); -}; + /** + * Apply a block an return the props + * + * @param {Object} inner + * @param {Object} context + * @return {Promise<Props>} + */ + toProps(inner, context) { + const processFn = this.getProcess(); + + inner = inner || {}; + inner.args = inner.args || []; + inner.kwargs = inner.kwargs || {}; + inner.blocks = inner.blocks || []; + + return Promise() + .then(() => processFn.call(context, inner)) + .then(props => { + if (is.string(props)) { + return { children: props }; + } + + return props; + }); + } -/** - * Convert a block props to HTML. This HTML is then being - * parsed by gitbook-core during rendering, and binded to the right react components. - * - * @param {Object} props - * @return {String} - */ -TemplateBlock.prototype.blockResultToHtml = function(props) { - const { children, ...innerProps } = props; - const payload = escape(JSON.stringify(innerProps)); + /** + * Convert a block props to HTML. This HTML is then being + * parsed by gitbook-core during rendering, and binded to the right react components. + * + * @param {Object} props + * @return {String} + */ + toHTML(props) { + const { children, ...innerProps } = props; + const payload = escape(JSON.stringify(innerProps)); + + return ( + `<${HTML_TAGNAME} name="${this.name}" props="${payload}">${children || ''}</${HTML_TAGNAME}>` + ); + } - return ( - `<${HTML_TAGNAME} name="${this.name}" props="${payload}">${children}</${HTML_TAGNAME}>` - ); -}; + /** + * Create a template block from a function or an object + * @param {String} blockName + * @param {Object} block + * @return {TemplateBlock} + */ + static create(blockName, block) { + if (is.fn(block)) { + block = new Map({ + process: block + }); + } -/** - * Create a template block from a function or an object - * @param {String} blockName - * @param {Object} block - * @return {TemplateBlock} - */ -TemplateBlock.create = function(blockName, block) { - if (is.fn(block)) { - block = new Immutable.Map({ - process: block - }); + block = new TemplateBlock(block); + block = block.set('name', blockName); + return block; } - - block = new TemplateBlock(block); - block = block.set('name', blockName); - return block; -}; +} /** * Extract kwargs from an arguments array diff --git a/packages/gitbook/src/models/templateEngine.js b/packages/gitbook/src/models/templateEngine.js index b218a9d..0d0dcb6 100644 --- a/packages/gitbook/src/models/templateEngine.js +++ b/packages/gitbook/src/models/templateEngine.js @@ -1,139 +1,133 @@ const nunjucks = require('nunjucks'); -const Immutable = require('immutable'); - -const TemplateEngine = Immutable.Record({ - // Map of {TemplateBlock} - blocks: Immutable.Map(), +const { Record, Map, List } = require('immutable'); +const DEFAULTS = { + // List of {TemplateBlock} + blocks: List(), // Map of Extension - extensions: Immutable.Map(), - + extensions: Map(), // Map of filters: {String} name -> {Function} fn - filters: Immutable.Map(), - + filters: Map(), // Map of globals: {String} name -> {Mixed} - globals: Immutable.Map(), - + globals: Map(), // Context for filters / blocks context: Object(), - // Nunjucks loader loader: nunjucks.FileSystemLoader('views') -}, 'TemplateEngine'); - -TemplateEngine.prototype.getBlocks = function() { - return this.get('blocks'); -}; - -TemplateEngine.prototype.getGlobals = function() { - return this.get('globals'); }; -TemplateEngine.prototype.getFilters = function() { - return this.get('filters'); -}; - -TemplateEngine.prototype.getShortcuts = function() { - return this.get('shortcuts'); -}; - -TemplateEngine.prototype.getLoader = function() { - return this.get('loader'); -}; - -TemplateEngine.prototype.getContext = function() { - return this.get('context'); -}; - -TemplateEngine.prototype.getExtensions = function() { - return this.get('extensions'); -}; - -/** - Return a block by its name (or undefined) - - @param {String} name - @return {TemplateBlock} -*/ -TemplateEngine.prototype.getBlock = function(name) { - const blocks = this.getBlocks(); - return blocks.find(function(block) { - return block.getName() === name; - }); -}; - -/** - Return a nunjucks environment from this configuration - - @return {Nunjucks.Environment} -*/ -TemplateEngine.prototype.toNunjucks = function(blocksOutput) { - const loader = this.getLoader(); - const blocks = this.getBlocks(); - const filters = this.getFilters(); - const globals = this.getGlobals(); - const extensions = this.getExtensions(); - const context = this.getContext(); - - const env = new nunjucks.Environment( - loader, - { - // Escaping is done after by the asciidoc/markdown parser - autoescape: false, - - // Syntax - tags: { - blockStart: '{%', - blockEnd: '%}', - variableStart: '{{', - variableEnd: '}}', - commentStart: '{###', - commentEnd: '###}' +class TemplateEngine extends Record(DEFAULTS) { + getBlocks() { + return this.get('blocks'); + } + + getGlobals() { + return this.get('globals'); + } + + getFilters() { + return this.get('filters'); + } + + getShortcuts() { + return this.get('shortcuts'); + } + + getLoader() { + return this.get('loader'); + } + + getContext() { + return this.get('context'); + } + + getExtensions() { + return this.get('extensions'); + } + + /** + * Return a block by its name (or undefined). + * @param {String} name + * @return {TemplateBlock} block? + */ + getBlock(name) { + const blocks = this.getBlocks(); + return blocks.find(function(block) { + return block.getName() === name; + }); + } + + /** + * Return a nunjucks environment from this configuration + * @return {Nunjucks.Environment} env + */ + toNunjucks() { + const loader = this.getLoader(); + const blocks = this.getBlocks(); + const filters = this.getFilters(); + const globals = this.getGlobals(); + const extensions = this.getExtensions(); + const context = this.getContext(); + + const env = new nunjucks.Environment( + loader, + { + // Escaping is done after by the asciidoc/markdown parser + autoescape: false, + + // Syntax + tags: { + blockStart: '{%', + blockEnd: '%}', + variableStart: '{{', + variableEnd: '}}', + commentStart: '{###', + commentEnd: '###}' + } } - } - ); - - // Add filters - filters.forEach(function(filterFn, filterName) { - env.addFilter(filterName, filterFn.bind(context)); - }); - - // Add blocks - blocks.forEach(function(block) { - const extName = block.getExtensionName(); - const Ext = block.toNunjucksExt(context, blocksOutput); - - env.addExtension(extName, new Ext()); - }); - - // Add globals - globals.forEach(function(globalValue, globalName) { - env.addGlobal(globalName, globalValue); - }); - - // Add other extensions - extensions.forEach(function(ext, extName) { - env.addExtension(extName, ext); - }); - - return env; -}; - -/** - Create a template engine - - @param {Object} def - @return {TemplateEngine} -*/ -TemplateEngine.create = function(def) { - return new TemplateEngine({ - blocks: Immutable.List(def.blocks || []), - extensions: Immutable.Map(def.extensions || {}), - filters: Immutable.Map(def.filters || {}), - globals: Immutable.Map(def.globals || {}), - context: def.context, - loader: def.loader - }); -}; + ); + + // Add filters + filters.forEach(function(filterFn, filterName) { + env.addFilter(filterName, filterFn.bind(context)); + }); + + // Add blocks + blocks.forEach(function(block) { + const extName = block.getExtensionName(); + const Ext = block.toNunjucksExt(context); + + env.addExtension(extName, new Ext()); + }); + + // Add globals + globals.forEach(function(globalValue, globalName) { + env.addGlobal(globalName, globalValue); + }); + + // Add other extensions + extensions.forEach(function(ext, extName) { + env.addExtension(extName, ext); + }); + + return env; + } + + /** + * Create a template engine. + * @param {Object} def + * @return {TemplateEngine} engine + */ + static create(def) { + return new TemplateEngine({ + blocks: List(def.blocks || []), + extensions: Map(def.extensions || {}), + filters: Map(def.filters || {}), + globals: Map(def.globals || {}), + context: def.context, + loader: def.loader + }); + } +} module.exports = TemplateEngine; diff --git a/packages/gitbook/src/parse/__tests__/parseURIIndexFromPages.js b/packages/gitbook/src/parse/__tests__/parseURIIndexFromPages.js index e3b9b55..755b225 100644 --- a/packages/gitbook/src/parse/__tests__/parseURIIndexFromPages.js +++ b/packages/gitbook/src/parse/__tests__/parseURIIndexFromPages.js @@ -3,7 +3,7 @@ const { OrderedMap } = require('immutable'); const parseURIIndexFromPages = require('../parseURIIndexFromPages'); const Page = require('../../models/page'); -describe.only('parseURIIndexFromPages', () => { +describe('parseURIIndexFromPages', () => { it('should map file to html', () => { const pages = OrderedMap({ @@ -14,24 +14,13 @@ describe.only('parseURIIndexFromPages', () => { expect(urls.resolve('page.md')).toBe('page.html'); }); - it('should map README to index.html (directoryIndex: false)', () => { - const pages = OrderedMap({ - 'hello/README.md': new Page() - }); - const urls = parseURIIndexFromPages(pages, { - directoryIndex: false - }); - - expect(urls.resolve('hello/README.md')).toBe('hello/index.html'); - }); - it('should map README to folder', () => { const pages = OrderedMap({ 'hello/README.md': new Page() }); const urls = parseURIIndexFromPages(pages); - expect(urls.resolve('hello/README.md')).toBe('hello/'); + expect(urls.resolveToURL('hello/README.md')).toBe('hello/'); }); }); diff --git a/packages/gitbook/src/templating/__tests__/conrefsLoader.js b/packages/gitbook/src/templating/__tests__/conrefsLoader.js index 431d0a3..18dd5dd 100644 --- a/packages/gitbook/src/templating/__tests__/conrefsLoader.js +++ b/packages/gitbook/src/templating/__tests__/conrefsLoader.js @@ -4,30 +4,34 @@ const TemplateEngine = require('../../models/templateEngine'); const renderTemplate = require('../render'); const ConrefsLoader = require('../conrefsLoader'); -describe('ConrefsLoader', function() { +describe('ConrefsLoader', () => { const dirName = __dirname + '/'; const fileName = path.join(dirName, 'test.md'); - describe('Git', function() { - const engine = TemplateEngine({ - loader: new ConrefsLoader(dirName) + describe('Git', () => { + let engine; + + before(() => { + engine = new TemplateEngine({ + loader: new ConrefsLoader(dirName) + }); }); - it('should include content from git', function() { + it('should include content from git', () => { return renderTemplate(engine, fileName, '{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md" %}') .then(function(out) { expect(out.getContent()).toBe('Hello from git'); }); }); - it('should handle deep inclusion (1)', function() { + it('should handle deep inclusion (1)', () => { return renderTemplate(engine, fileName, '{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test2.md" %}') .then(function(out) { expect(out.getContent()).toBe('First Hello. Hello from git'); }); }); - it('should handle deep inclusion (2)', function() { + it('should handle deep inclusion (2)', () => { return renderTemplate(engine, fileName, '{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test3.md" %}') .then(function(out) { expect(out.getContent()).toBe('First Hello. Hello from git'); @@ -35,20 +39,24 @@ describe('ConrefsLoader', function() { }); }); - describe('Local', function() { - const engine = TemplateEngine({ - loader: new ConrefsLoader(dirName) + describe('Local', () => { + let engine; + + before(() => { + engine = new TemplateEngine({ + loader: new ConrefsLoader(dirName) + }); }); - describe('Relative', function() { - it('should resolve basic relative filepath', function() { + describe('Relative', () => { + it('should resolve basic relative filepath', () => { return renderTemplate(engine, fileName, '{% include "include.md" %}') .then(function(out) { expect(out.getContent()).toBe('Hello World'); }); }); - it('should resolve basic parent filepath', function() { + it('should resolve basic parent filepath', () => { return renderTemplate(engine, path.join(dirName, 'hello/test.md'), '{% include "../include.md" %}') .then(function(out) { expect(out.getContent()).toBe('Hello World'); @@ -57,23 +65,24 @@ describe('ConrefsLoader', function() { }); describe('Absolute', function() { - it('should resolve absolute filepath', function() { + it('should resolve absolute filepath', () => { return renderTemplate(engine, fileName, '{% include "/include.md" %}') .then(function(out) { expect(out.getContent()).toBe('Hello World'); }); }); - it('should resolve absolute filepath when in a directory', function() { + it('should resolve absolute filepath when in a directory', () => { return renderTemplate(engine, path.join(dirName, 'hello/test.md'), '{% include "/include.md" %}') .then(function(out) { expect(out.getContent()).toBe('Hello World'); }); }); }); + }); - describe('transform', function() { + describe('transform', () => { function transform(filePath, source) { expect(filePath).toBeA('string'); expect(source).toBeA('string'); @@ -83,11 +92,16 @@ describe('ConrefsLoader', function() { return 'test-' + source + '-endtest'; } - const engine = TemplateEngine({ - loader: new ConrefsLoader(dirName, transform) + + let engine; + + before(() => { + engine = new TemplateEngine({ + loader: new ConrefsLoader(dirName, transform) + }); }); - it('should transform included content', function() { + it('should transform included content', () => { return renderTemplate(engine, fileName, '{% include "include.md" %}') .then(function(out) { expect(out.getContent()).toBe('test-Hello World-endtest'); @@ -95,4 +109,3 @@ describe('ConrefsLoader', function() { }); }); }); - |