Merge pull request #67 from patrick-steele-idem/circular

Fixes #66 - Allow circular dependencies when loading templates
This commit is contained in:
ramahadevan 2015-04-23 10:57:07 -07:00
commit 9fa2e6b7ba
15 changed files with 85 additions and 10 deletions

View File

@ -22,6 +22,26 @@ function notEmpty(o) {
return true;
}
function createLazyRenderer(handler) {
var lazyRenderer = function(input, out) {
lazyRenderer.renderer(input, out);
};
// This is the initial function that will do the rendering. We replace
// the renderer with the actual renderer func on the first render
lazyRenderer.renderer = function(input, out) {
var rendererFunc = handler.renderer || handler.render;
if (typeof renderFunc !== 'function') {
throw new Error('Invalid tag handler: ' + handler);
}
// Use the actual renderer from now on
lazyRenderer.renderer = rendererFunc;
rendererFunc(input, out);
};
return lazyRenderer;
}
var WARNED_INVOKE_BODY = 0;
module.exports = {
@ -116,8 +136,12 @@ module.exports = {
r: function(handler) {
var renderFunc = handler.renderer || handler.render || handler;
// If the user code has a circular function then the renderer function
// may not be available on the module. Since we can't get a reference
// to the actual renderer(input, out) function right now we lazily
// try to get access to it later.
if (typeof renderFunc !== 'function') {
throw new Error('Invalid tag handler: ' + handler);
return createLazyRenderer(handler);
}
return renderFunc;

View File

@ -53,8 +53,8 @@ if (streamPath) {
stream = require(streamPath);
}
function Template(renderFunc, options) {
this._ = renderFunc;
function Template( options) {
this._ = null;
this.buffer = !options || options.buffer !== false;
}
@ -204,20 +204,35 @@ function load(templatePath, options) {
if (typeof templatePath === 'string') {
template = cache[templatePath];
if (!template) {
// The template has not been loaded, load the template to get
// access to the factory function that is used to produce
// The template has not been loaded
// Cache the Template instance before actually loading and initializing
// the compiled template. This allows circular dependencies since the
// partially loaded Template instance will be found in the cache.
template = cache[templatePath] = new Template(options);
// Now load the template to get access to the factory function that is used to produce
// the actual compiled template function. We pass the helpers
// as the first argument to the factory function to produce
// the compiled template function
template = cache[templatePath] = new Template(
loader(templatePath).create(helpers), // Load the template factory and invoke it
options);
// the template rendering function
template._ = loader(templatePath).create(helpers); // Load the template factory and invoke it
}
} else {
// Instead of a path, assume we got a compiled template module
// We store the loaded template with the factory function that was
// used to get access to the compiled template function
template = templatePath._ || (templatePath._ = new Template(templatePath.create(helpers), options));
template = templatePath._;
if (!template) {
// First put the partially loaded Template instance on the
// the compiled template module before actually loading and
// initializing the compiled template. This allows for circular
// dependencies during template loading.
template = templatePath._ = new Template(options);
// Now fully initialize the template by adding the needed render
// function.
template._ = templatePath.create(helpers);
}
}
return template;

View File

@ -102,6 +102,18 @@
"attributes": {
"name": "string"
}
},
"test-circular-renderer-a": {
"renderer": "./taglib/test-circular-renderer-a/renderer"
},
"test-circular-renderer-b": {
"renderer": "./taglib/test-circular-renderer-b/renderer"
},
"test-circular-template-a": {
"template": "./taglib/test-circular-template-a/template.marko"
},
"test-circular-template-b": {
"template": "./taglib/test-circular-template-b/template.marko"
}
},
"tags-dir": "./taglib/scanned-tags",

View File

@ -0,0 +1,6 @@
var marko = require('../../../../');
var template = marko.load(require.resolve('./template.marko'));
exports.renderer = function(input, out) {
template.render({}, out);
};

View File

@ -0,0 +1,2 @@
test-circular-renderer-a
<test-circular-renderer-b/>

View File

@ -0,0 +1,6 @@
var marko = require('../../../../');
marko.load(require.resolve('./template.marko'));
exports.renderer = function(input, out) {
out.write('test-circular-renderer-b');
};

View File

@ -0,0 +1 @@
<test-circular-renderer-a/>

View File

@ -0,0 +1,2 @@
test-circular-template-a
<test-circular-template-b/>

View File

@ -0,0 +1 @@
test-circular-template-b<test-circular-template-a if="false"/>

View File

@ -0,0 +1 @@
test-circular-renderer-a test-circular-renderer-b

View File

@ -0,0 +1 @@
<test-circular-renderer-a/>

View File

@ -0,0 +1 @@
exports.templateData = {};

View File

@ -0,0 +1 @@
test-circular-template-a test-circular-template-b

View File

@ -0,0 +1 @@
<test-circular-template-a/>

View File

@ -0,0 +1 @@
exports.templateData = {};