diff --git a/runtime/OutMixins.js b/runtime/RenderResult.js similarity index 76% rename from runtime/OutMixins.js rename to runtime/RenderResult.js index 2de6218435..e1a2cd4c19 100644 --- a/runtime/OutMixins.js +++ b/runtime/RenderResult.js @@ -1,14 +1,14 @@ var events = require('./events'); var dom = require('./dom'); -function checkAddedToDOM(asyncStream, method) { - if (!asyncStream.data._added) { +function checkAddedToDOM(result, method) { + if (!result.out.data._added) { throw new Error('Cannot call ' + method + '() until after HTML fragment is added to DOM.'); } } -function getWidgetDefs(asyncStream) { - var widgetDefs = asyncStream.data.widgets; +function getWidgetDefs(result) { + var widgetDefs = result.out.data.widgets; if (!widgetDefs || widgetDefs.length === 0) { throw new Error('No widget rendered'); @@ -16,11 +16,15 @@ function getWidgetDefs(asyncStream) { return widgetDefs; } -module.exports = { +var RenderResult = module.exports = function RenderResult(out) { + this.out = out; +}; + +RenderResult.prototype = { getWidget: function() { checkAddedToDOM(this, 'getWidget'); - var rerenderWidget = this.global.__rerenderWidget; + var rerenderWidget = this.out.global.__rerenderWidget; if (rerenderWidget) { return rerenderWidget; } @@ -54,46 +58,24 @@ module.exports = { }, afterInsert: function(node) { - var data = this.data; + var data = this.out.data; data._added = true; - var widgetsContext = this.global.widgets; + var widgetsContext = this.out.global.widgets; var widgetDefs = widgetsContext ? widgetsContext.widgets : null; data.widgets = widgetDefs; events.emit('mountNode', { node: node, - out: this, + result: this, + out: this.out, document: node.ownerDocument }); // NOTE: This will trigger widgets to be initialized if there were any return this; }, - then: function(fn, fnErr) { - var self = this; - var promise = new Promise(function(resolve, reject) { - self.on('error', reject); - self.on('finish', function(data) { - try { - resolve(fn(data)); - } catch(err) { - reject(err); - } - }); - }); - - if (fnErr) { - promise = promise.catch(fnErr); - } - return promise; - }, - - catch: function(fnErr) { - return this.then(undefined, fnErr); - }, - appendTo: function(referenceEl) { var newNode = this.getNode(referenceEl.ownerDocument); dom.appendTo(newNode, referenceEl); @@ -124,5 +106,17 @@ module.exports = { dom.prependTo(newNode, referenceEl); return this.afterInsert(newNode); }, + getNode: function() { + return this.out.getNode(); + }, + getOutput: function() { + return this.out.getOutput(); + }, + toString: function() { + return this.out.toString(); + }, + toJSON: function() { + return this.getOutput(); + }, document: typeof document !== 'undefined' && document }; \ No newline at end of file diff --git a/runtime/html/AsyncStream.js b/runtime/html/AsyncStream.js index 4270028173..8edf74d8a8 100644 --- a/runtime/html/AsyncStream.js +++ b/runtime/html/AsyncStream.js @@ -2,8 +2,8 @@ var EventEmitter = require('events').EventEmitter; var StringWriter = require('./StringWriter'); var BufferedWriter = require('./BufferedWriter'); -var extend = require('raptor-util/extend'); var documentProvider = require('../document-provider'); +var RenderResult = require('../RenderResult'); var helpers; var voidWriter = { write:function(){} }; @@ -471,6 +471,22 @@ var proto = AsyncStream.prototype = { return this.getOutput(); }, + then: function(fn, fnErr) { + var out = this; + var promise = new Promise(function(resolve, reject) { + out.on('error', reject); + out.on('finish', function() { + resolve(new RenderResult(out)); + }); + }); + + return Promise.resolve(promise).then(fn, fnErr); + }, + + catch: function(fnErr) { + return this.then(undefined, fnErr); + }, + // END DOM METHODS // Deprecated BEGIN: @@ -498,8 +514,6 @@ var proto = AsyncStream.prototype = { // alias: proto.w = proto.write; -extend(proto, require('../OutMixins')); - module.exports = AsyncStream; helpers = require('./helpers'); \ No newline at end of file diff --git a/runtime/html/index.js b/runtime/html/index.js index 9cd29d166c..b5a75dfe3b 100644 --- a/runtime/html/index.js +++ b/runtime/html/index.js @@ -1,6 +1,7 @@ 'use strict'; // helpers provide a core set of various utility methods to compiled templates var helpers; +var RenderResult = require('../RenderResult'); /** * Method is for internal usage only. This method @@ -49,7 +50,7 @@ Template.prototype = { out.sync(); this._(localData, out); - return out.getOutput(); + return new RenderResult(out); }, /** @@ -94,7 +95,7 @@ Template.prototype = { if (callback) { out .on('finish', function() { - callback(null, out.getOutput(), out); + callback(null, new RenderResult(out), out); }) .once('error', callback); } @@ -119,7 +120,7 @@ Template.prototype = { if (callback) { finalOut .on('finish', function() { - callback(null, finalOut.getOutput(), finalOut); + callback(null, new RenderResult(finalOut), finalOut); }) .once('error', callback); } diff --git a/runtime/vdom/AsyncVDOMBuilder.js b/runtime/vdom/AsyncVDOMBuilder.js index dff8d45f0a..104a7f3012 100644 --- a/runtime/vdom/AsyncVDOMBuilder.js +++ b/runtime/vdom/AsyncVDOMBuilder.js @@ -3,9 +3,9 @@ var HTMLElement = require('./HTMLElement'); var DocumentFragment = require('./DocumentFragment'); var Comment = require('./Comment'); var Text = require('./Text'); -var extend = require('raptor-util/extend'); var virtualizeHTML = require('./virtualizeHTML'); var documentProvider = require('../document-provider'); +var RenderResult = require('../RenderResult'); function State(tree) { this.remaining = 1; @@ -139,7 +139,7 @@ var proto = AsyncVDOMBuilder.prototype = { if (!remaining) { state.finished = true; - state.events.emit('finish', state.tree); + state.events.emit('finish', this); } return this; @@ -175,7 +175,7 @@ var proto = AsyncVDOMBuilder.prototype = { flush: function() { var state = this._state; - state.events.emit('update', state.tree); + state.events.emit('update', this); }, getOutput: function() { @@ -186,7 +186,7 @@ var proto = AsyncVDOMBuilder.prototype = { var state = this._state; if (event === 'finish' && state.finished) { - callback(state.tree); + callback(this); return this; } @@ -198,7 +198,7 @@ var proto = AsyncVDOMBuilder.prototype = { var state = this._state; if (event === 'finish' && state.finished) { - callback(state.tree); + callback(this); return this; } @@ -266,6 +266,7 @@ var proto = AsyncVDOMBuilder.prototype = { lastArray.push(callback); return this; }, + getNode: function(doc) { var node = this._node; if (!node) { @@ -291,6 +292,22 @@ var proto = AsyncVDOMBuilder.prototype = { return node; }, + then: function(fn, fnErr) { + var out = this; + var promise = new Promise(function(resolve, reject) { + out.on('error', reject); + out.on('finish', function() { + resolve(new RenderResult(out)); + }); + }); + + return Promise.resolve(promise).then(fn, fnErr); + }, + + catch: function(fnErr) { + return this.then(undefined, fnErr); + }, + isVDOM: true }; @@ -300,6 +317,4 @@ proto.ee = proto.endElement; proto.t = proto.text; proto.h = proto.write = proto.html; -extend(proto, require('../OutMixins')); - module.exports = AsyncVDOMBuilder; \ No newline at end of file diff --git a/runtime/vdom/index.js b/runtime/vdom/index.js index 5dff288681..6eda788165 100644 --- a/runtime/vdom/index.js +++ b/runtime/vdom/index.js @@ -2,6 +2,7 @@ // helpers provide a core set of various utility methods // that are available in every template var helpers; +var RenderResult = require('../RenderResult'); /** * Method is for internal usage only. This method @@ -9,7 +10,7 @@ var helpers; * it is used to create a new Template instance. * @private */ - exports.c = function createTemplate(path) { +exports.c = function createTemplate(path) { return new Template(path); }; @@ -45,7 +46,7 @@ Template.prototype = { var out = new AsyncVDOMBuilder(globalData); out.sync(); this._(localData, out); - return out.getOutput(); + return new RenderResult(out); }, /** @@ -90,7 +91,7 @@ Template.prototype = { if (callback) { out .on('finish', function() { - callback(null, out.getOutput(), out); + callback(null, new RenderResult(out), out); }) .once('error', callback); } @@ -113,7 +114,7 @@ Template.prototype = { if (callback) { finalOut .on('finish', function() { - callback(null, finalOut.getOutput(), finalOut); + callback(null, new RenderResult(finalOut), out); }) .once('error', callback); } diff --git a/test/AsyncStream-test.js b/test/AsyncStream-test.js index 9466326054..10ae1e3cf4 100644 --- a/test/AsyncStream-test.js +++ b/test/AsyncStream-test.js @@ -31,7 +31,7 @@ function createAsyncStream(options) { } } -describe('async-writer' , function() { +describe('AsyncStream', function() { beforeEach(function(done) { // for (var k in require.cache) { @@ -63,10 +63,10 @@ describe('async-writer' , function() { out.write('1'); out.write('2'); - return out.end().then((data) => { + return out.end().then((result) => { const output = out.getOutput(); expect(output).to.equal('12'); - expect(data.getOutput()).to.equal('12'); + expect(result.toString()).to.equal('12'); }); }); diff --git a/test/AsyncVDOMBuilder-test.js b/test/AsyncVDOMBuilder-test.js index 3c8019bf4b..e47308007b 100644 --- a/test/AsyncVDOMBuilder-test.js +++ b/test/AsyncVDOMBuilder-test.js @@ -2,128 +2,133 @@ var AsyncVDOMBuilder = require('../runtime/vdom/AsyncVDOMBuilder'); var HTMLElement = require('../runtime/vdom/HTMLElement'); var expect = require('chai').expect; -it('sync', function() { - var out = new AsyncVDOMBuilder(); - out.element('div', {}, 0); - var tree = out.getOutput(); - expect(tree.childNodes.length).to.equal(1); -}); - -it('end, then listen for finish', function(done) { - var out = new AsyncVDOMBuilder(); - out.element('div', {}, 0); - out.end(); - out.on('finish', function(tree) { +describe('AsyncVDOMBuilder', function() { + it('sync', function() { + var out = new AsyncVDOMBuilder(); + out.element('div', {}, 0); + var tree = out.getOutput(); expect(tree.childNodes.length).to.equal(1); - done(); }); -}); - -it('async', function(done) { - var out = new AsyncVDOMBuilder(); - out.element('div', {}, 0); - var asyncOut = out.beginAsync(); - - setTimeout(function() { - asyncOut.element('span', {}, 0); - asyncOut.end(); - }, 10); - - out.element('section', {}, 0); - - out.end(); - out.on('finish', function(tree) { - expect(tree.childNodes.length).to.equal(3); - expect(tree.firstChild.nodeName).to.equal('div'); - expect(tree.firstChild.nextSibling.nodeName).to.equal('span'); - expect(tree.firstChild.nextSibling.nextSibling.nodeName).to.equal('section'); - done(); - }); -}); -it('promise', function(done) { - const out = new AsyncVDOMBuilder(); - out.element('div', {}, 0); - out.end().then((tree) => { - expect(tree.childNodes.length).to.equal(1); - done(); + it('end, then listen for finish', function(done) { + var out = new AsyncVDOMBuilder(); + out.element('div', {}, 0); + out.end(); + out.on('finish', function(result) { + expect(result.getOutput().childNodes.length).to.equal(1); + done(); + }); }); -}); -it('async flush', function(done) { - var out = new AsyncVDOMBuilder(); - out.on('update', function(tree) { - expect(tree.childNodes.length).to.equal(1); - }); - out.once('finish', function(tree) { - expect(tree.childNodes.length).to.equal(2); - done(); + it('async', function(done) { + var out = new AsyncVDOMBuilder(); + out.element('div', {}, 0); + var asyncOut = out.beginAsync(); + + setTimeout(function() { + asyncOut.element('span', {}, 0); + asyncOut.end(); + }, 10); + + out.element('section', {}, 0); + + out.end(); + out.on('finish', function(result) { + var tree = result.getOutput(); + expect(tree.childNodes.length).to.equal(3); + expect(tree.firstChild.nodeName).to.equal('div'); + expect(tree.firstChild.nextSibling.nodeName).to.equal('span'); + expect(tree.firstChild.nextSibling.nextSibling.nodeName).to.equal('section'); + done(); + }); }); - out.element('div', {}, 0); - out.flush(); - - var asyncOut = out.beginAsync(); - - setTimeout(function() { - asyncOut.element('span', {}, 0); - asyncOut.end(); - }, 10); - - out.end(); -}); - -it('for loop', function(done) { - var out = new AsyncVDOMBuilder(); - out.once('finish', function(tree) { - var header = tree.childNodes[0]; - var list = tree.childNodes[1]; - var paragraph = tree.childNodes[2]; - expect(header.nodeName).to.equal('h1'); - expect(list.nodeName).to.equal('ul'); - expect(list.childNodes.length).to.equal(10); - expect(paragraph.nodeName).to.equal('p'); - done(); + it('promise', function(done) { + const out = new AsyncVDOMBuilder(); + out.element('div', {}, 0); + out.end().then((result) => { + expect(result.getOutput().childNodes.length).to.equal(1); + done(); + }).catch(done); }); - out.element('h1', {}, 0); + it('async flush', function(done) { + var out = new AsyncVDOMBuilder(); + out.on('update', function(result) { + expect(result.getOutput().childNodes.length).to.equal(1); + }); + out.once('finish', function(result) { + expect(result.getOutput().childNodes.length).to.equal(2); + done(); + }); + + out.element('div', {}, 0); + out.flush(); - out.beginElement('ul', {}); + var asyncOut = out.beginAsync(); - for(var i = 0; i < 10; i++) { - out.element('li', {}, 1).t(i); - } + setTimeout(function() { + asyncOut.element('span', {}, 0); + asyncOut.end(); + }, 10); - out.endElement(); + out.end(); + }); - out.element('p', {}, 0); + it('for loop', function(done) { + var out = new AsyncVDOMBuilder(); + out.once('finish', function(result) { + var tree = result.getOutput(); + var header = tree.childNodes[0]; + var list = tree.childNodes[1]; + var paragraph = tree.childNodes[2]; + expect(header.nodeName).to.equal('h1'); + expect(list.nodeName).to.equal('ul'); + expect(list.childNodes.length).to.equal(10); + expect(paragraph.nodeName).to.equal('p'); + done(); + }); - out.end(); -}); + out.element('h1', {}, 0); -it('staticNode, text, comment', function(done) { - var staticNode = new HTMLElement('div', {}, 0, 'f891ea3'); - var out = new AsyncVDOMBuilder(); + out.beginElement('ul', {}); - out.node(staticNode); - out.text('Hello World'); - out.comment('TODO: make this work'); - out.end(); + for(var i = 0; i < 10; i++) { + out.element('li', {}, 1).t(i); + } - out.once('finish', function(tree) { - expect(tree.childNodes[0].nodeName).to.equal('div'); - expect(tree.childNodes[1].nodeValue).to.equal('Hello World'); - expect(tree.childNodes[2].nodeValue).to.equal('TODO: make this work'); - done(); + out.endElement(); + + out.element('p', {}, 0); + + out.end(); }); -}); - -// it('should handle timeouts correctly'); -// -// it('should handle sync errors correctly'); -// -// it('should handle timeout errors correctly'); -// -// it('should avoid writes after end'); -// -// it('globals'); \ No newline at end of file + + it('staticNode, text, comment', function(done) { + var staticNode = new HTMLElement('div', {}, 0, 'f891ea3'); + var out = new AsyncVDOMBuilder(); + + out.node(staticNode); + out.text('Hello World'); + out.comment('TODO: make this work'); + out.end(); + + out.once('finish', function(result) { + var tree = result.getOutput(); + expect(tree.childNodes[0].nodeName).to.equal('div'); + expect(tree.childNodes[1].nodeValue).to.equal('Hello World'); + expect(tree.childNodes[2].nodeValue).to.equal('TODO: make this work'); + done(); + }); + }); + + // it('should handle timeouts correctly'); + // + // it('should handle sync errors correctly'); + // + // it('should handle timeout errors correctly'); + // + // it('should avoid writes after end'); + // + // it('globals'); +}); \ No newline at end of file diff --git a/test/async-fragments-deprecated-test.js b/test/async-fragments-deprecated-test.js index 45eed75ec6..72d04db4f4 100644 --- a/test/async-fragments-deprecated-test.js +++ b/test/async-fragments-deprecated-test.js @@ -67,11 +67,13 @@ describe('async-fragments (deprecated)', function() { addEventListener('asyncFragmentBeforeRender'); addEventListener('asyncFragmentFinish'); - template.render(templateData, out, function(err, html) { + template.render(templateData, out, function(err, result) { if (err) { return done(err); } + var html = result.toString(); + if (main.checkHtml) { main.checkHtml(html); } else { diff --git a/test/autotests/api/load-render-callback/test.js b/test/autotests/api/load-render-callback/test.js index 17415f31ab..f65ea6703f 100644 --- a/test/autotests/api/load-render-callback/test.js +++ b/test/autotests/api/load-render-callback/test.js @@ -5,13 +5,13 @@ exports.check = function(marko, markoCompiler, expect, done) { template.render({ name: 'John' }, - function(err, html, out) { + function(err, result, out) { if (err) { return done(err); } - expect(html).to.equal(out.getOutput()); - expect(html).to.equal('Hello John!'); + expect(result.toString()).to.equal(out.getOutput()); + expect(result.toString()).to.equal('Hello John!'); done(); }); }; \ No newline at end of file diff --git a/test/autotests/api/load-render-promise/test.js b/test/autotests/api/load-render-promise/test.js index 656f6e1050..285e8b8f67 100644 --- a/test/autotests/api/load-render-promise/test.js +++ b/test/autotests/api/load-render-promise/test.js @@ -7,8 +7,8 @@ exports.check = function(marko, markoCompiler, expect, done) { template.render({ name: 'John' - }).then((out) => { - expect(out.getOutput()).to.equal('Hello John!'); + }).then((result) => { + expect(result.toString()).to.equal('Hello John!'); done(); }).catch((err) => { done(err); diff --git a/test/autotests/api/load-source/test.js b/test/autotests/api/load-source/test.js index 321c020ea5..e2e72aba2c 100644 --- a/test/autotests/api/load-source/test.js +++ b/test/autotests/api/load-source/test.js @@ -8,12 +8,12 @@ exports.check = function(marko, markoCompiler, expect, done) { // Make sure calling load with templatePath:String, templateSrc:String arguments works templatePath = nodePath.join(__dirname, 'dummy.marko'); template = marko.load(templatePath, '- Hello $!{data.name}!'); - expect(template.renderSync({name: 'Frank'})).to.equal('Hello Frank!'); + expect(template.renderSync({name: 'Frank'}).toString()).to.equal('Hello Frank!'); // Make sure calling load with templatePath:String, templateSrc:String, options:Object arguments works templatePath = nodePath.join(__dirname, 'dummy.marko'); template = marko.load(templatePath, '- Hello $!{data.name}!', {}); - expect(template.renderSync({name: 'Frank'})).to.equal('Hello Frank!'); + expect(template.renderSync({name: 'Frank'}).toString()).to.equal('Hello Frank!'); // Make sure calling load with templatePath:String, options:Object arguments works delete markoCompiler.defaultOptions.writeToDisk; @@ -30,6 +30,6 @@ exports.check = function(marko, markoCompiler, expect, done) { template = marko.load(templatePath, {writeToDisk: false}); expect(fs.existsSync(compiledPath)).to.equal(false); expect(template.render).to.be.a('function'); - expect(template.renderSync({name: 'Frank'})).to.equal('Hello Frank!'); + expect(template.renderSync({name: 'Frank'}).toString()).to.equal('Hello Frank!'); done(); }; \ No newline at end of file diff --git a/test/autotests/api/no-write-to-disk-load/test.js b/test/autotests/api/no-write-to-disk-load/test.js index 3618543560..a209ef8b87 100644 --- a/test/autotests/api/no-write-to-disk-load/test.js +++ b/test/autotests/api/no-write-to-disk-load/test.js @@ -9,7 +9,7 @@ exports.check = function(marko, markoCompiler, expect, done) { var template = marko.load(templatePath); expect(fs.existsSync(compiledPath)).to.equal(false); expect(template.render).to.be.a('function'); - expect(template.renderSync({name: 'Frank'})).to.equal('Hello Frank!'); + expect(template.renderSync({name: 'Frank'}).toString()).to.equal('Hello Frank!'); } finally { markoCompiler.defaultOptions.writeToDisk = true; } diff --git a/test/autotests/api/no-write-to-disk-require/test.js b/test/autotests/api/no-write-to-disk-require/test.js index d4f03116c8..135687a03b 100644 --- a/test/autotests/api/no-write-to-disk-require/test.js +++ b/test/autotests/api/no-write-to-disk-require/test.js @@ -9,7 +9,7 @@ exports.check = function(marko, markoCompiler, expect, done) { var template = require(templatePath); expect(fs.existsSync(compiledPath)).to.equal(false); expect(template.render).to.be.a('function'); - expect(template.renderSync({name: 'Frank'})).to.equal('Hello Frank!'); + expect(template.renderSync({name: 'Frank'}).toString()).to.equal('Hello Frank!'); } finally { markoCompiler.defaultOptions.writeToDisk = true; } diff --git a/test/autotests/api/render-callback-args/test.js b/test/autotests/api/render-callback-args/test.js index 36adc137be..7cf091d15f 100644 --- a/test/autotests/api/render-callback-args/test.js +++ b/test/autotests/api/render-callback-args/test.js @@ -3,8 +3,8 @@ exports.check = function(marko, markoCompiler, expect, done) { var data = { name: 'John' }; - template.render(data, function(error, html, out) { - expect(html).to.equal('Hello John!'); + template.render(data, function(error, result, out) { + expect(result.toString()).to.equal('Hello John!'); expect(out != null).to.equal(true); done(); }); diff --git a/test/autotests/api/render-callback-global-data/test.js b/test/autotests/api/render-callback-global-data/test.js index 0029dea063..0bbdbe9cd4 100644 --- a/test/autotests/api/render-callback-global-data/test.js +++ b/test/autotests/api/render-callback-global-data/test.js @@ -8,8 +8,8 @@ exports.check = function(marko, markoCompiler, expect, done) { greeting: 'Greetings' } }; - template.render(data, function(error, output) { - expect(output).to.equal('Greetings John!'); + template.render(data, function(error, result) { + expect(result.toString()).to.equal('Greetings John!'); done(); }); }; \ No newline at end of file diff --git a/test/autotests/api/renderSync-global-data/test.js b/test/autotests/api/renderSync-global-data/test.js index 099eec03d9..7680ff669b 100644 --- a/test/autotests/api/renderSync-global-data/test.js +++ b/test/autotests/api/renderSync-global-data/test.js @@ -8,7 +8,7 @@ exports.check = function(marko, markoCompiler, expect, done) { greeting: 'Greetings' } }; - var output = template.renderSync(data); - expect(output).to.equal('Greetings John!'); + var result = template.renderSync(data); + expect(result.toString()).to.equal('Greetings John!'); done(); }; \ No newline at end of file diff --git a/test/autotests/api/renderSync-no-data/test.js b/test/autotests/api/renderSync-no-data/test.js index 129454a953..e14cde5ad5 100644 --- a/test/autotests/api/renderSync-no-data/test.js +++ b/test/autotests/api/renderSync-no-data/test.js @@ -2,7 +2,7 @@ var nodePath = require('path'); exports.check = function(marko, markoCompiler, expect, done) { var template = marko.load(nodePath.join(__dirname, 'template.marko')); - var output = template.renderSync(); - expect(output).to.equal('Hello!'); + var result = template.renderSync(); + expect(result.toString()).to.equal('Hello!'); done(); }; \ No newline at end of file diff --git a/test/autotests/api/renderSync/test.js b/test/autotests/api/renderSync/test.js index edacda64d8..bef36ccd22 100644 --- a/test/autotests/api/renderSync/test.js +++ b/test/autotests/api/renderSync/test.js @@ -2,7 +2,7 @@ var nodePath = require('path'); exports.check = function(marko, markoCompiler, expect, done) { var template = marko.load(nodePath.join(__dirname, 'template.marko')); - var output = template.renderSync({ name: 'John' }); - expect(output).to.equal('Hello John!'); + var result = template.renderSync({ name: 'John' }); + expect(result.toString()).to.equal('Hello John!'); done(); }; \ No newline at end of file diff --git a/test/autotests/api/require-compiled-template/test.js b/test/autotests/api/require-compiled-template/test.js index e9659aa82c..8dcd96ff2e 100644 --- a/test/autotests/api/require-compiled-template/test.js +++ b/test/autotests/api/require-compiled-template/test.js @@ -10,12 +10,12 @@ exports.check = function(marko, markoCompiler, expect, done) { template.render({ name: 'John' }, - function(err, output) { + function(err, result) { if (err) { return done(err); } - expect(output).to.equal('Hello John!'); + expect(result.toString()).to.equal('Hello John!'); done(); }); }; \ No newline at end of file diff --git a/test/autotests/api/require-render-callback/test.js b/test/autotests/api/require-render-callback/test.js index c53bd952c8..96dfee544d 100644 --- a/test/autotests/api/require-render-callback/test.js +++ b/test/autotests/api/require-render-callback/test.js @@ -7,12 +7,12 @@ exports.check = function(marko, markoCompiler, expect, done) { { name: 'John' }, - function(err, output) { + function(err, result) { if (err) { return done(err); } - expect(output).to.equal('Hello John!'); + expect(result.toString()).to.equal('Hello John!'); done(); }); }; \ No newline at end of file diff --git a/test/autotests/api/write-to-disk-load/test.js b/test/autotests/api/write-to-disk-load/test.js index 7bc10e9c4b..500e7dfbcc 100644 --- a/test/autotests/api/write-to-disk-load/test.js +++ b/test/autotests/api/write-to-disk-load/test.js @@ -8,7 +8,7 @@ exports.check = function(marko, markoCompiler, expect, done) { compiledPath = nodePath.join(__dirname, 'template.marko.js'); var template = marko.load(templatePath); expect(fs.existsSync(compiledPath)).to.equal(true); - expect(template.renderSync({name: 'Frank'})).to.equal('Hello Frank!'); + expect(template.renderSync({name: 'Frank'}).toString()).to.equal('Hello Frank!'); done(); }; \ No newline at end of file diff --git a/test/autotests/api/write-to-disk-require/test.js b/test/autotests/api/write-to-disk-require/test.js index 3623c31d59..737925fdc2 100644 --- a/test/autotests/api/write-to-disk-require/test.js +++ b/test/autotests/api/write-to-disk-require/test.js @@ -10,7 +10,7 @@ exports.check = function(marko, markoCompiler, expect, done) { var template = require(templatePath); delete require.cache[templatePath]; expect(fs.existsSync(compiledPath)).to.equal(true); - expect(template.renderSync({name: 'Frank'})).to.equal('Hello Frank!'); + expect(template.renderSync({name: 'Frank'}).toString()).to.equal('Hello Frank!'); } finally { fs.unlinkSync(compiledPath); } diff --git a/test/autotests/api/writeToDisk-false-include/test.js b/test/autotests/api/writeToDisk-false-include/test.js index cac03a571b..4e8bf20f06 100644 --- a/test/autotests/api/writeToDisk-false-include/test.js +++ b/test/autotests/api/writeToDisk-false-include/test.js @@ -9,7 +9,7 @@ exports.check = function(marko, markoCompiler, expect, done) { var templatePath = nodePath.join(__dirname, 'template.marko'); var template = marko.load(templatePath); - expect(template.renderSync({name: 'Frank'})).to.equal('Hello Frank!'); + expect(template.renderSync({name: 'Frank'}).toString()).to.equal('Hello Frank!'); markoCompiler.configure({ writeToDisk: false diff --git a/test/autotests/api/writeToDisk-false-layout/test.js b/test/autotests/api/writeToDisk-false-layout/test.js index 9c57bfb081..8f3b720272 100644 --- a/test/autotests/api/writeToDisk-false-layout/test.js +++ b/test/autotests/api/writeToDisk-false-layout/test.js @@ -9,7 +9,7 @@ exports.check = function(marko, markoCompiler, expect, done) { var templatePath = nodePath.join(__dirname, 'template.marko'); var template = marko.load(templatePath); - expect(template.renderSync({name: 'Frank'})).to.equal('
Hello Frank!
'); + expect(template.renderSync({name: 'Frank'}).toString()).to.equal('
Hello Frank!
'); markoCompiler.configure({ writeToDisk: false diff --git a/test/autotests/hot-reload/load/test.js b/test/autotests/hot-reload/load/test.js index 1ac34335a5..c086d687ca 100644 --- a/test/autotests/hot-reload/load/test.js +++ b/test/autotests/hot-reload/load/test.js @@ -10,13 +10,13 @@ exports.check = function(marko, hotReload, expect) { var template = marko.load(tempTemplatePath); - expect(template.renderSync({ name: 'John' })).to.equal('Hello John!'); + expect(template.renderSync({ name: 'John' }).toString()).to.equal('Hello John!'); fs.writeFileSync(tempTemplatePath, templateSrc + '!', { encoding: 'utf8' }); - expect(template.renderSync({ name: 'John' })).to.equal('Hello John!'); + expect(template.renderSync({ name: 'John' }).toString()).to.equal('Hello John!'); hotReload.handleFileModified(tempTemplatePath); - expect(template.renderSync({ name: 'John' })).to.equal('Hello John!!'); + expect(template.renderSync({ name: 'John' }).toString()).to.equal('Hello John!!'); }; \ No newline at end of file diff --git a/test/autotests/hot-reload/require/test.js b/test/autotests/hot-reload/require/test.js index 1951fc7706..88b0343a91 100644 --- a/test/autotests/hot-reload/require/test.js +++ b/test/autotests/hot-reload/require/test.js @@ -10,13 +10,13 @@ exports.check = function(marko, hotReload, expect) { var template = require(tempTemplatePath); - expect(template.renderSync({ name: 'John' })).to.equal('Hello John!'); + expect(template.renderSync({ name: 'John' }).toString()).to.equal('Hello John!'); fs.writeFileSync(tempTemplatePath, templateSrc + '!', { encoding: 'utf8' }); - expect(template.renderSync({ name: 'John' })).to.equal('Hello John!'); + expect(template.renderSync({ name: 'John' }).toString()).to.equal('Hello John!'); hotReload.handleFileModified(tempTemplatePath); - expect(template.renderSync({ name: 'John' })).to.equal('Hello John!!'); + expect(template.renderSync({ name: 'John' }).toString()).to.equal('Hello John!!'); }; \ No newline at end of file diff --git a/test/autotests/inline/multiple-templates-custom-tags/index.js b/test/autotests/inline/multiple-templates-custom-tags/index.js index 56247104d7..49a7cd4319 100644 --- a/test/autotests/inline/multiple-templates-custom-tags/index.js +++ b/test/autotests/inline/multiple-templates-custom-tags/index.js @@ -9,5 +9,5 @@ module.exports = function() { test-bar test-bar`; - return template1.renderSync() + '\n' + template2.renderSync(); + return template1.renderSync().toString() + '\n' + template2.renderSync().toString(); }; \ No newline at end of file diff --git a/test/autotests/widgets-browser-deprecated/widget-render-to-iframe/index.js b/test/autotests/widgets-browser-deprecated/widget-render-to-iframe/index.js index a3818f3a16..24ec0d0591 100644 --- a/test/autotests/widgets-browser-deprecated/widget-render-to-iframe/index.js +++ b/test/autotests/widgets-browser-deprecated/widget-render-to-iframe/index.js @@ -5,7 +5,7 @@ module.exports = require('marko/widgets').defineComponent({ renderIntoIframe: function() { var frameEl = this.getFrameEl(); - return iframeContentComponent.render({}) + return iframeContentComponent.renderSync({}) .appendTo(frameEl.contentWindow.document.body) .getWidget(); }, diff --git a/test/autotests/widgets-browser/widget-render-to-iframe/index.js b/test/autotests/widgets-browser/widget-render-to-iframe/index.js index a3818f3a16..24ec0d0591 100644 --- a/test/autotests/widgets-browser/widget-render-to-iframe/index.js +++ b/test/autotests/widgets-browser/widget-render-to-iframe/index.js @@ -5,7 +5,7 @@ module.exports = require('marko/widgets').defineComponent({ renderIntoIframe: function() { var frameEl = this.getFrameEl(); - return iframeContentComponent.render({}) + return iframeContentComponent.renderSync({}) .appendTo(frameEl.contentWindow.document.body) .getWidget(); }, diff --git a/test/autotests/widgets-pages/server-browser-unique-ids/tests.js b/test/autotests/widgets-pages/server-browser-unique-ids/tests.js index 7a2ea89ad4..d3d8190ade 100644 --- a/test/autotests/widgets-pages/server-browser-unique-ids/tests.js +++ b/test/autotests/widgets-pages/server-browser-unique-ids/tests.js @@ -6,7 +6,7 @@ describe(path.basename(__dirname), function() { it('should generate a unique ID that is different for a UI component rendered on the server and browser', function() { var serverFooWidget = window.fooWidget; - var browserFooWidget = appFooComponent.render({}).appendTo(document.body).getWidget(); + var browserFooWidget = appFooComponent.renderSync({}).appendTo(document.body).getWidget(); expect(browserFooWidget.id).to.be.a('string'); expect(serverFooWidget.id).to.be.a('string'); expect(serverFooWidget.id).to.not.equal(browserFooWidget.id); diff --git a/test/inline-test.js b/test/inline-test.js index 7147ce55d9..2250e31d27 100644 --- a/test/inline-test.js +++ b/test/inline-test.js @@ -35,22 +35,22 @@ describe('inline', function() { var func = require(outputFile); - function handleOutput(html) { - helpers.compare(html, '.html'); + function handleOutput(result) { + helpers.compare(result.toString(), '.html'); done(); } if (func.length === 1) { - func((err, html) => { + func((err, result) => { if (err) { return done(err); } - handleOutput(html); + handleOutput(result); }); } else { - let outputHtml = func(); - handleOutput(outputHtml); + let result = func(); + handleOutput(result); } diff --git a/test/util/BrowserHelpers.js b/test/util/BrowserHelpers.js index a5c81ee1a5..5b235efb22 100644 --- a/test/util/BrowserHelpers.js +++ b/test/util/BrowserHelpers.js @@ -29,7 +29,7 @@ BrowserHelpers.prototype = { }, mount: function(component, input) { - var renderResult = component.render(input) + var renderResult = component.renderSync(input) .appendTo(this.targetEl); var widget; diff --git a/test/util/runRenderTest.js b/test/util/runRenderTest.js index 5b58ee8302..a961fba45a 100644 --- a/test/util/runRenderTest.js +++ b/test/util/runRenderTest.js @@ -140,11 +140,13 @@ module.exports = function runRenderTest(dir, helpers, done, options) { asyncEventsVerifier = createAsyncVerifier(main, helpers, out); } - template.render(templateData, out, function(err, renderOutput) { + template.render(templateData, out, function(err, renderResult) { if (err) { return done(err); } + var renderOutput = renderResult.getOutput(); + if (isVDOM) { let vdomTree = renderOutput; diff --git a/widgets/Widget.js b/widgets/Widget.js index b3c4fe41dc..d9725f4676 100644 --- a/widgets/Widget.js +++ b/widgets/Widget.js @@ -3,6 +3,7 @@ var inherit = require('raptor-util/inherit'); var dom = require('./dom'); var markoWidgets = require('./'); var EventEmitter = require('events').EventEmitter; +var RenderResult = require('../runtime/RenderResult'); var listenerTracker = require('listener-tracker'); var arrayFromArguments = require('raptor-util/arrayFromArguments'); var extend = require('raptor-util/extend'); @@ -551,6 +552,7 @@ Widget.prototype = widgetProto = { var createOut = renderer.createOut || marko.createOut; var out = createOut(globalData); renderer(templateData, out); + var result = new RenderResult(out); var targetNode; @@ -633,7 +635,7 @@ Widget.prototype = widgetProto = { }); // Trigger any 'onUpdate' events for all of the rendered widgets - out.afterInsert(elToReplace); + result.afterInsert(elToReplace); self.__lifecycleState = null; diff --git a/widgets/defineRenderer.js b/widgets/defineRenderer.js index ce5c94fb4e..9c5ca1f447 100644 --- a/widgets/defineRenderer.js +++ b/widgets/defineRenderer.js @@ -1,5 +1,6 @@ var marko = require('marko'); var extend = require('raptor-util/extend'); +var RenderResult = require('../runtime/RenderResult'); module.exports = function defineRenderer(def) { var renderer = def.renderer; @@ -169,10 +170,29 @@ module.exports = function defineRenderer(def) { renderer.createOut = createOut; - renderer.render = function(input) { + renderer.render = function(input, cb) { var out = createOut(); renderer(input, out); - return out.end(); + out.end(); + + if(cb) { + out.on('finished', function() { + cb(null, new RenderResult(out)); + }); + out.on('error', function(err) { + cb(err); + }); + } + + return out; + }; + + renderer.renderSync = function(input) { + var out = createOut(); + out.sync(); + renderer(input, out); + out.end(); + return new RenderResult(out); }; return renderer; diff --git a/widgets/defineWidget-browser.js b/widgets/defineWidget-browser.js index a4673a4898..78289d9627 100644 --- a/widgets/defineWidget-browser.js +++ b/widgets/defineWidget-browser.js @@ -26,6 +26,7 @@ module.exports = function defineWidget(def, renderer) { return { renderer: renderer, render: renderer.render, + renderSync: renderer.renderSync, extendWidget: function(widget) { extendWidget(widget); widget.renderer = renderer; @@ -105,6 +106,7 @@ module.exports = function defineWidget(def, renderer) { // new widget constructor function Widget.renderer = proto.renderer = renderer; Widget.render = renderer.render; + Widget.renderSync = renderer.renderSync; } return Widget; diff --git a/widgets/defineWidget.js b/widgets/defineWidget.js index b6a1bdaf65..1eaacea4d5 100644 --- a/widgets/defineWidget.js +++ b/widgets/defineWidget.js @@ -23,7 +23,8 @@ module.exports = function defineWidget(def, renderer) { return { _isWidget: true, renderer: renderer, - render: renderer.render + render: renderer.render, + renderSync: renderer.renderSync }; } else { return {_isWidget: true}; diff --git a/widgets/renderable.js b/widgets/renderable.js index 29d9bc6065..32dca8f14e 100644 --- a/widgets/renderable.js +++ b/widgets/renderable.js @@ -1,13 +1,34 @@ var marko = require('marko'); +var RenderResult = require('../runtime/RenderResult'); module.exports = function(target, renderer) { var rendererFunc = renderer.renderer || renderer.render || renderer; var createOut = renderer.createOut || marko.createOut; target.renderer = rendererFunc; - target.render = function(input) { + + target.render = function(input, cb) { + var out = createOut(); + rendererFunc(input, out); + out.end(); + + if(cb) { + out.on('finished', function() { + cb(null, new RenderResult(out)); + }); + out.on('error', function(err) { + cb(err); + }); + } + + return out; + }; + + target.renderSync = function(input) { var out = createOut(); + out.sync(); rendererFunc(input, out); - return out.end(); + out.end(); + return new RenderResult(out); }; }; \ No newline at end of file