summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/models/book.js10
-rw-r--r--lib/output/createTemplateEngine.js2
-rw-r--r--lib/output/website/createTemplateEngine.js7
-rw-r--r--lib/output/website/listSearchPaths.js23
-rw-r--r--lib/output/website/onAsset.js2
-rw-r--r--lib/templating/index.js3
-rw-r--r--lib/templating/themesLoader.js117
-rw-r--r--lib/utils/path.js15
8 files changed, 173 insertions, 6 deletions
diff --git a/lib/models/book.js b/lib/models/book.js
index 9b9f769..eb8011b 100644
--- a/lib/models/book.js
+++ b/lib/models/book.js
@@ -105,6 +105,16 @@ Book.prototype.getRoot = function() {
};
/**
+ Return root for content of the book
+
+ @return {String}
+*/
+Book.prototype.getContentRoot = function() {
+ var fs = this.getContentFS();
+ return fs.getRoot();
+};
+
+/**
Check if a file is ignore (should not being parsed, etc)
@param {String} ref
diff --git a/lib/output/createTemplateEngine.js b/lib/output/createTemplateEngine.js
index a79f93f..9b29b2a 100644
--- a/lib/output/createTemplateEngine.js
+++ b/lib/output/createTemplateEngine.js
@@ -16,7 +16,7 @@ var defaultFilters = require('../constants/defaultFilters');
function createTemplateEngine(output) {
var plugins = output.getPlugins();
var book = output.getBook();
- var rootFolder = book.getRoot();
+ var rootFolder = book.getContentRoot();
var logger = book.getLogger();
var filters = plugins
diff --git a/lib/output/website/createTemplateEngine.js b/lib/output/website/createTemplateEngine.js
index 9aed746..1a70667 100644
--- a/lib/output/website/createTemplateEngine.js
+++ b/lib/output/website/createTemplateEngine.js
@@ -1,4 +1,6 @@
+var Templating = require('../../templating');
var TemplateEngine = require('../../models/templateEngine');
+var listSearchPaths = require('./listSearchPaths');
/**
Create templating engine to render themes
@@ -7,8 +9,11 @@ var TemplateEngine = require('../../models/templateEngine');
@return {TemplateEngine}
*/
function createTemplateEngine(output) {
- return new TemplateEngine({
+ var searchPaths = listSearchPaths(output);
+ var loader = new Templating.ThemesLoader(searchPaths);
+ return new TemplateEngine({
+ loader: loader
});
}
diff --git a/lib/output/website/listSearchPaths.js b/lib/output/website/listSearchPaths.js
new file mode 100644
index 0000000..c45f39c
--- /dev/null
+++ b/lib/output/website/listSearchPaths.js
@@ -0,0 +1,23 @@
+
+/**
+ List search paths for templates / i18n, etc
+
+ @param {Output} output
+ @return {List<String>}
+*/
+function listSearchPaths(output) {
+ var book = output.getBook();
+ var plugins = output.getPlugins();
+
+ var searchPaths = plugins
+ .valueSeq()
+ .map(function(plugin) {
+ return plugin.getPath();
+ })
+ .toList();
+
+ return searchPaths.unshift(book.getContentRoot());
+}
+
+
+module.exports = listSearchPaths;
diff --git a/lib/output/website/onAsset.js b/lib/output/website/onAsset.js
index a2342ae..8659ca6 100644
--- a/lib/output/website/onAsset.js
+++ b/lib/output/website/onAsset.js
@@ -11,7 +11,7 @@ function onAsset(output, asset) {
var book = output.getBook();
var options = output.getOptions();
- var rootFolder = book.getRoot();
+ var rootFolder = book.getContentRoot();
var outputFolder = options.get('root');
var filePath = path.resolve(rootFolder, asset);
diff --git a/lib/templating/index.js b/lib/templating/index.js
index 4cdf6ef..a33965d 100644
--- a/lib/templating/index.js
+++ b/lib/templating/index.js
@@ -4,5 +4,6 @@ module.exports = {
renderFile: require('./renderFile'),
postRender: require('./postRender'),
- ConrefsLoader: require('./conrefsLoader')
+ ConrefsLoader: require('./conrefsLoader'),
+ ThemesLoader: require('./themesLoader')
};
diff --git a/lib/templating/themesLoader.js b/lib/templating/themesLoader.js
new file mode 100644
index 0000000..2b436a4
--- /dev/null
+++ b/lib/templating/themesLoader.js
@@ -0,0 +1,117 @@
+var Immutable = require('immutable');
+var nunjucks = require('nunjucks');
+var fs = require('fs');
+var path = require('path');
+
+var PathUtils = require('../utils/path');
+
+/**
+
+*/
+var ThemesLoader = nunjucks.Loader.extend({
+ init: function(searchPaths) {
+ this.searchPaths = Immutable.List(searchPaths)
+ .map(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;
+ }
+
+ var src = fs.readFileSync(fullpath, 'utf-8');
+
+ src = '{% do %}var template = template || {}; template.stack = template.stack || []; template.stack.push(template.self); template.self = ' + JSON.stringify(templateName) + '{% enddo %}\n' +
+ src +
+ '\n{% do %}template.self = template.stack.pop();{% enddo %}';
+
+ return {
+ src: src,
+ path: fullpath,
+ noCache: true
+ };
+ },
+
+ /*
+ 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 this.searchPaths
+ .sortBy(function(s) {
+ return -s.length;
+ })
+ .find(function(basePath) {
+ return (filepath && filepath.indexOf(basePath) === 0);
+ });
+ },
+
+ /*
+ 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;
+
+ // Relative template like "./test.html"
+ if (PathUtils.isPureRelative(to) && from) {
+ return path.resolve(path.dirname(from), to);
+ }
+
+ // Determine in which search folder we currently are
+ 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
+ if (originalFilename == to) {
+ var currentIndex = searchPaths.indexOf(originalSearchPath);
+ searchPaths = searchPaths.slice(currentIndex + 1);
+ }
+
+ // Absolute template to resolve in root folder
+ var resultFolder = searchPaths.find(function(basePath) {
+ var p = path.resolve(basePath, to);
+
+ return (
+ p.indexOf(basePath) === 0
+ && fs.existsSync(p)
+ );
+ });
+ if (!resultFolder) return null;
+ return path.resolve(resultFolder, to);
+ }
+});
+
+module.exports = ThemesLoader;
diff --git a/lib/utils/path.js b/lib/utils/path.js
index c233c92..a4968c8 100644
--- a/lib/utils/path.js
+++ b/lib/utils/path.js
@@ -42,7 +42,7 @@ function resolveInRoot(root) {
return result;
}
-// Chnage extension
+// Chnage extension of a file
function setExtension(filename, ext) {
return path.join(
path.dirname(filename),
@@ -50,9 +50,20 @@ function setExtension(filename, ext) {
);
}
+/*
+ Return true if a filename is relative.
+
+ @param {String}
+ @return {Boolean}
+*/
+function isPureRelative(filename) {
+ return (filename.indexOf('./') === 0 || filename.indexOf('../') === 0);
+}
+
module.exports = {
isInRoot: isInRoot,
resolveInRoot: resolveInRoot,
normalize: normalizePath,
- setExtension: setExtension
+ setExtension: setExtension,
+ isPureRelative: isPureRelative
};