diff --git a/runtime/helpers.js b/runtime/helpers.js
index c8a9f51d7..2560d5b6a 100644
--- a/runtime/helpers.js
+++ b/runtime/helpers.js
@@ -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;
diff --git a/runtime/marko-runtime.js b/runtime/marko-runtime.js
index b1d8c0d59..d223222da 100644
--- a/runtime/marko-runtime.js
+++ b/runtime/marko-runtime.js
@@ -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;
diff --git a/test/fixtures/marko-taglib.json b/test/fixtures/marko-taglib.json
index e980348f2..a833f77c6 100644
--- a/test/fixtures/marko-taglib.json
+++ b/test/fixtures/marko-taglib.json
@@ -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",
diff --git a/test/fixtures/taglib/test-circular-renderer-a/renderer.js b/test/fixtures/taglib/test-circular-renderer-a/renderer.js
new file mode 100644
index 000000000..a3f5fa902
--- /dev/null
+++ b/test/fixtures/taglib/test-circular-renderer-a/renderer.js
@@ -0,0 +1,6 @@
+var marko = require('../../../../');
+var template = marko.load(require.resolve('./template.marko'));
+
+exports.renderer = function(input, out) {
+ template.render({}, out);
+};
\ No newline at end of file
diff --git a/test/fixtures/taglib/test-circular-renderer-a/template.marko b/test/fixtures/taglib/test-circular-renderer-a/template.marko
new file mode 100644
index 000000000..bb7f23745
--- /dev/null
+++ b/test/fixtures/taglib/test-circular-renderer-a/template.marko
@@ -0,0 +1,2 @@
+test-circular-renderer-a
+
\ No newline at end of file
diff --git a/test/fixtures/taglib/test-circular-renderer-b/renderer.js b/test/fixtures/taglib/test-circular-renderer-b/renderer.js
new file mode 100644
index 000000000..871a65c80
--- /dev/null
+++ b/test/fixtures/taglib/test-circular-renderer-b/renderer.js
@@ -0,0 +1,6 @@
+var marko = require('../../../../');
+marko.load(require.resolve('./template.marko'));
+
+exports.renderer = function(input, out) {
+ out.write('test-circular-renderer-b');
+};
\ No newline at end of file
diff --git a/test/fixtures/taglib/test-circular-renderer-b/template.marko b/test/fixtures/taglib/test-circular-renderer-b/template.marko
new file mode 100644
index 000000000..e6820b206
--- /dev/null
+++ b/test/fixtures/taglib/test-circular-renderer-b/template.marko
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/fixtures/taglib/test-circular-template-a/template.marko b/test/fixtures/taglib/test-circular-template-a/template.marko
new file mode 100644
index 000000000..d364b2bd9
--- /dev/null
+++ b/test/fixtures/taglib/test-circular-template-a/template.marko
@@ -0,0 +1,2 @@
+test-circular-template-a
+
\ No newline at end of file
diff --git a/test/fixtures/taglib/test-circular-template-b/template.marko b/test/fixtures/taglib/test-circular-template-b/template.marko
new file mode 100644
index 000000000..436327d0d
--- /dev/null
+++ b/test/fixtures/taglib/test-circular-template-b/template.marko
@@ -0,0 +1 @@
+test-circular-template-b
\ No newline at end of file
diff --git a/test/fixtures/templates/circular-renderer/expected.html b/test/fixtures/templates/circular-renderer/expected.html
new file mode 100644
index 000000000..54d975a00
--- /dev/null
+++ b/test/fixtures/templates/circular-renderer/expected.html
@@ -0,0 +1 @@
+test-circular-renderer-a test-circular-renderer-b
\ No newline at end of file
diff --git a/test/fixtures/templates/circular-renderer/template.marko b/test/fixtures/templates/circular-renderer/template.marko
new file mode 100644
index 000000000..e6820b206
--- /dev/null
+++ b/test/fixtures/templates/circular-renderer/template.marko
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/fixtures/templates/circular-renderer/test.js b/test/fixtures/templates/circular-renderer/test.js
new file mode 100644
index 000000000..c4013b344
--- /dev/null
+++ b/test/fixtures/templates/circular-renderer/test.js
@@ -0,0 +1 @@
+exports.templateData = {};
diff --git a/test/fixtures/templates/circular-template/expected.html b/test/fixtures/templates/circular-template/expected.html
new file mode 100644
index 000000000..aa3d40f06
--- /dev/null
+++ b/test/fixtures/templates/circular-template/expected.html
@@ -0,0 +1 @@
+test-circular-template-a test-circular-template-b
\ No newline at end of file
diff --git a/test/fixtures/templates/circular-template/template.marko b/test/fixtures/templates/circular-template/template.marko
new file mode 100644
index 000000000..d54ac0006
--- /dev/null
+++ b/test/fixtures/templates/circular-template/template.marko
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/fixtures/templates/circular-template/test.js b/test/fixtures/templates/circular-template/test.js
new file mode 100644
index 000000000..c4013b344
--- /dev/null
+++ b/test/fixtures/templates/circular-template/test.js
@@ -0,0 +1 @@
+exports.templateData = {};