summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/gitbook/src/models/__tests__/page.js5
-rw-r--r--packages/gitbook/src/models/__tests__/templateBlock.js98
-rw-r--r--packages/gitbook/src/models/__tests__/uriIndex.js14
-rw-r--r--packages/gitbook/src/models/templateBlock.js410
-rw-r--r--packages/gitbook/src/models/templateEngine.js244
-rw-r--r--packages/gitbook/src/parse/__tests__/parseURIIndexFromPages.js15
-rw-r--r--packages/gitbook/src/templating/__tests__/conrefsLoader.js53
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="{&quot;message&quot;:&quot;p Samy&quot;}"></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() {
});
});
});
-