Merge branch 'perf' of github.com:patrick-steele-idem/marko

Conflicts:
	package.json
This commit is contained in:
Patrick Steele-Idem 2015-06-05 11:27:25 -06:00
commit 209a4de099
9 changed files with 160 additions and 112 deletions

View File

@ -5,6 +5,16 @@ Changelog
## 2.5.x
### 2.6.0
- Performance improvements
- `'use strict';`
- Optimized render code paths
- Code cleanup
- Compatibility fixes for Node.js 0.12
- Bad: `fs.readFile(path, 'utf8')`
- Good: `fs.readFile(path, {encoding: 'utf8'})`
### 2.5.0
- Fixes #78 - Custom Node.js require extension for Marko template files. Example usage:

View File

@ -369,7 +369,7 @@ template.render({
```javascript
var template = require('marko').load('template.marko');
var out = require('fs').createWriteStream('index.html', 'utf8');
var out = require('fs').createWriteStream('index.html', {encoding: 'utf8'});
// Render the template to 'index.html'
template.stream({
@ -383,7 +383,7 @@ Alternatively, you can render directly to an existing stream to avoid creating a
```javascript
var template = require('marko').load('template.marko');
var out = require('fs').createWriteStream('index.html', 'utf8');
var out = require('fs').createWriteStream('index.html', {encoding: 'utf8'});
// Render the template to 'index.html'
template.render({
@ -414,7 +414,7 @@ var fs = require('fs');
var marko = require('marko');
var template = marko.load('template.marko');
var out = marko.createWriter(fs.createWriteStream('index.html', 'utf8'));
var out = marko.createWriter(fs.createWriteStream('index.html', {encoding: 'utf8'}));
// Render the first chunk asynchronously (after 1s delay):
var asyncOut = out.beginAsync();

View File

@ -491,6 +491,11 @@ TemplateBuilder.prototype = {
} else {
params = ['out'];
}
// Don't use "use strict" in compiled templates since it
// could break backwards compatibility.
// out.append('"use strict";\n');
out.append('function create(__helpers) {\n');
//Write out the static variables
this.writer.flush();

View File

@ -3,6 +3,7 @@ var fs = require('fs');
var jsonminify = require('jsonminify');
var tagDefFromCode = require('./tag-def-from-code');
var loader = require('./loader');
var fsReadOptions = { encoding: 'utf8' };
function createDefaultTagDef() {
return {
@ -62,7 +63,7 @@ module.exports = function scanTagsDir(tagsConfigPath, tagsConfigDirname, dir, ta
if (fs.existsSync(tagFile)) {
// marko-tag.json exists in the directory, use that as the tag definition
tagDef = JSON.parse(jsonminify(fs.readFileSync(tagFile, {encoding: 'utf8'})));
tagDef = JSON.parse(jsonminify(fs.readFileSync(tagFile, fsReadOptions)));
if (!tagDef.renderer && !tagDef.template) {
if (fs.existsSync(rendererFile)) {
tagDef.renderer = rendererFile;
@ -95,7 +96,7 @@ module.exports = function scanTagsDir(tagsConfigPath, tagsConfigDirname, dir, ta
exTemplateFile = templateFile + ".html";
}
if(exTemplateFile){
var templateCode = fs.readFileSync(exTemplateFile, {encoding: 'utf8'});
var templateCode = fs.readFileSync(exTemplateFile, fsReadOptions);
tagDef = tagDefFromCode.extractTagDef(templateCode);
if (!tagDef) {
tagDef = createDefaultTagDef();
@ -106,7 +107,7 @@ module.exports = function scanTagsDir(tagsConfigPath, tagsConfigDirname, dir, ta
}
if (rendererJSFile) {
var rendererCode = fs.readFileSync(rendererJSFile, {encoding: 'utf8'});
var rendererCode = fs.readFileSync(rendererJSFile, fsReadOptions);
tagDef = tagDefFromCode.extractTagDef(rendererCode);
if (!tagDef) {
tagDef = createDefaultTagDef();

View File

@ -1,8 +1,9 @@
var fs = require('fs');
var jsonminify = require('jsonminify');
var fsReadOptions = { encoding: 'utf8' };
exports.readTaglib = function (path) {
var json = fs.readFileSync(path, 'utf8');
var json = fs.readFileSync(path, fsReadOptions);
try {
var taglibProps = JSON.parse(jsonminify(json));

View File

@ -1,67 +1,67 @@
{
"name": "marko",
"description": "Marko is an extensible, streaming, asynchronous, high performance, HTML-based templating language that can be used in Node.js or in the browser.",
"keywords": [
"templating",
"template",
"async",
"streaming"
],
"repository": {
"type": "git",
"url": "https://github.com/raptorjs/marko.git"
},
"scripts": {
"test": "node_modules/.bin/mocha --ui bdd --reporter spec ./test && node_modules/.bin/jshint compiler/ runtime/ taglibs/",
"test-fast": "node_modules/.bin/mocha --ui bdd --reporter spec ./test/render-test",
"test-async": "node_modules/.bin/mocha --ui bdd --reporter spec ./test/render-async-test",
"test-taglib-loader": "node_modules/.bin/mocha --ui bdd --reporter spec ./test/taglib-loader-test",
"jshint": "node_modules/.bin/jshint compiler/ runtime/ taglibs/"
},
"author": "Patrick Steele-Idem <pnidem@gmail.com>",
"maintainers": [
"Patrick Steele-Idem <pnidem@gmail.com>"
],
"dependencies": {
"app-module-path": "^1.0.0",
"async-writer": "^1.1.2",
"browser-refresh-client": "^1.0.0",
"char-props": "~0.1.5",
"events": "^1.0.2",
"htmlparser2": "^3.7.2",
"jsonminify": "^0.2.3",
"marko-async": "^2.0.0",
"marko-layout": "^2.0.0",
"minimatch": "^0.2.14",
"property-handlers": "^1.0.0",
"raptor-args": "^1.0.0",
"raptor-json": "^1.0.1",
"raptor-logging": "^1.0.1",
"raptor-modules": "^1.0.5",
"raptor-polyfill": "^1.0.0",
"raptor-promises": "^1.0.1",
"raptor-regexp": "^1.0.0",
"raptor-strings": "^1.0.0",
"raptor-util": "^1.0.0",
"resolve-from": "^1.0.0",
"sax": "^0.6.0"
},
"devDependencies": {
"chai": "~1.8.1",
"dustjs-linkedin": "^2.3.4",
"jshint": "^2.5.0",
"mocha": "~1.15.1",
"raptor-cache": "^1.1.1",
"raptor-data-providers": "^1.0.1-beta",
"through": "^2.3.4"
},
"license": "Apache License v2.0",
"bin": {
"markoc": "bin/markoc"
},
"main": "runtime/marko-runtime.js",
"publishConfig": {
"registry": "https://registry.npmjs.org/"
},
"version": "2.5.2"
}
"name": "marko",
"description": "Marko is an extensible, streaming, asynchronous, high performance, HTML-based templating language that can be used in Node.js or in the browser.",
"keywords": [
"templating",
"template",
"async",
"streaming"
],
"repository": {
"type": "git",
"url": "https://github.com/raptorjs/marko.git"
},
"scripts": {
"test": "node_modules/.bin/mocha --ui bdd --reporter spec ./test && node_modules/.bin/jshint compiler/ runtime/ taglibs/",
"test-fast": "node_modules/.bin/mocha --ui bdd --reporter spec ./test/render-test",
"test-async": "node_modules/.bin/mocha --ui bdd --reporter spec ./test/render-async-test",
"test-taglib-loader": "node_modules/.bin/mocha --ui bdd --reporter spec ./test/taglib-loader-test",
"jshint": "node_modules/.bin/jshint compiler/ runtime/ taglibs/"
},
"author": "Patrick Steele-Idem <pnidem@gmail.com>",
"maintainers": [
"Patrick Steele-Idem <pnidem@gmail.com>"
],
"dependencies": {
"app-module-path": "^1.0.0",
"async-writer": "^1.4.0",
"browser-refresh-client": "^1.0.0",
"char-props": "~0.1.5",
"events": "^1.0.2",
"htmlparser2": "^3.7.2",
"jsonminify": "^0.2.3",
"marko-async": "^2.0.0",
"marko-layout": "^2.0.0",
"minimatch": "^0.2.14",
"property-handlers": "^1.0.0",
"raptor-args": "^1.0.0",
"raptor-json": "^1.0.1",
"raptor-logging": "^1.0.1",
"raptor-modules": "^1.0.5",
"raptor-polyfill": "^1.0.0",
"raptor-promises": "^1.0.1",
"raptor-regexp": "^1.0.0",
"raptor-strings": "^1.0.0",
"raptor-util": "^1.0.0",
"resolve-from": "^1.0.0",
"sax": "^0.6.0"
},
"devDependencies": {
"chai": "~1.8.1",
"dustjs-linkedin": "^2.3.4",
"jshint": "^2.5.0",
"mocha": "~1.15.1",
"raptor-cache": "^1.1.1",
"raptor-data-providers": "^1.0.1-beta",
"through": "^2.3.4"
},
"license": "Apache-2.0",
"bin": {
"markoc": "bin/markoc"
},
"main": "runtime/marko-runtime.js",
"publishConfig": {
"registry": "https://registry.npmjs.org/"
},
"version": "2.6.0"
}

View File

@ -1,3 +1,4 @@
'use strict';
var escapeXml = require('raptor-util/escapeXml');
var escapeXmlAttr = escapeXml.attr;
var runtime = require('./'); // Circular dependnecy, but that is okay

View File

@ -3,6 +3,7 @@ var fs = require('fs');
var Module = require('module').Module;
var markoCompiler = require('../compiler');
var cwd = process.cwd();
var fsReadOptions = {encoding: 'utf8'};
if (process.env.hasOwnProperty('MARKO_HOT_RELOAD')) {
require('../hot-reload').enable();
@ -42,14 +43,14 @@ module.exports = function load(templatePath) {
return require(targetFile);
}
var templateSrc = fs.readFileSync(templatePath, {encoding: 'utf8'});
var templateSrc = fs.readFileSync(templatePath, fsReadOptions);
var compiledSrc = compiler.compile(templateSrc);
// console.log('Compiled code for "' + templatePath + '":\n' + compiledSrc);
var filename = nodePath.basename(targetFile);
var tempFile = nodePath.join(targetDir, '.' + process.pid + '.' + Date.now() + '.' + filename);
fs.writeFileSync(tempFile, compiledSrc, {encoding: 'utf8'});
fs.writeFileSync(tempFile, compiledSrc, fsReadOptions);
fs.renameSync(tempFile, targetFile);
return require(targetFile);

View File

@ -25,7 +25,7 @@
// async-writer provides all of the magic to support asynchronous
// rendering to a stream
'use strict';
/**
* Method is for internal usage only. This method
* is invoked by code in a compiled Marko template and
@ -36,6 +36,8 @@ exports.c = function createTemplate(path) {
return new Template(path);
};
var BUFFER_OPTIONS = { buffer: true };
var asyncWriter = require('async-writer');
// helpers can the core set of utility methods
@ -67,10 +69,25 @@ if (streamPath) {
stream = require(streamPath);
}
function renderCallback(renderFunc, data, globalData, callback) {
var out = new AsyncWriter();
if (globalData) {
extend(out.global, globalData);
}
renderFunc(data, out);
return out.end()
.on('finish', function() {
callback(null, out.getOutput(), out);
})
.once('error', callback);
}
function Template(path, func, options) {
this.path = path;
this._ = func;
this.buffer = !options || options.buffer !== false;
this._options = !options || options.buffer !== false ?
BUFFER_OPTIONS : null;
}
Template.prototype = {
@ -96,67 +113,83 @@ Template.prototype = {
}
this._(data, out);
out.end();
return out.getOutput();
},
/**
* Renders a template to either a stream (if the last
* argument is a Stream instance) or
* provides the output to a callback function (if the last
* argument is a Function).
*
* Supported signatures:
*
* render(data, callback)
* render(data, out)
* render(data, stream)
* render(data, out, callback)
* render(data, stream, callback)
*
* @param {Object} data The view model data for the template
* @param {AsyncWriter} out A Stream or an AsyncWriter instance
* @param {Function} callback A callback function
* @return {AsyncWriter} Returns the AsyncWriter instance that the template is rendered to
*/
render: function(data, out) {
render: function(data, out, callback) {
var renderFunc = this._;
var finalData;
var globalData;
if (data) {
finalData = data;
if ((globalData = data.$global)) {
// We will *move* the "$global" property
// into the "out.global" object
delete data.$global;
}
} else {
finalData = {};
}
if (typeof out === 'function') {
// Short circuit for render(data, callback)
return renderCallback(renderFunc, finalData, globalData, out);
}
// NOTE: We create new vars here to avoid a V8 de-optimization due
// to the following:
// Assignment to parameter in arguments object
var finalOut = out;
var finalData = data || {};
var renderFunc = this._;
// callback is last argument if provided
var callback = arguments[arguments.length - 1];
var shouldEnd = false;
if (typeof callback === 'function') {
if (arguments.length === 2) {
// render called with data and callback,
// we need to create the "out"
finalOut = null;
}
if (arguments.length === 3) {
// render(data, out, callback)
if (!finalOut || !finalOut.isAsyncWriter) {
finalOut = new AsyncWriter(finalOut);
shouldEnd = true;
}
finalOut.on('finish', function() {
callback(null, finalOut.getOutput(), finalOut);
});
finalOut.once('error', callback);
finalOut
.on('finish', function() {
callback(null, finalOut.getOutput(), finalOut);
})
.once('error', callback);
} else if (!finalOut || !finalOut.isAsyncWriter) {
var stream = finalOut;
// Assume the "finalOut" is really a stream
//
// By default, we will buffer rendering to a stream to prevent
// the response from being "too chunky".
var options = this.buffer ? { buffer: true } : null;
finalOut = asyncWriter.create(stream, options);
finalOut = asyncWriter.create(finalOut, this._options);
shouldEnd = true;
}
if (finalData.$global) {
finalOut.global = extend(finalOut.global, finalData.$global);
delete finalData.$global;
if (globalData) {
extend(out.global, globalData);
}
// Invoke the compiled template's render function to have it
// write out strings to the provided out.
renderFunc(finalData, finalOut);
// Automatically end output stream (the writer) if we
@ -167,27 +200,23 @@ Template.prototype = {
// If out parameter was originally an AsyncWriter then
// we assume that we are writing to output that was
// created in the context of another rendering job.
if (shouldEnd) {
finalOut.end();
}
return finalOut;
return shouldEnd ? finalOut.end() : finalOut;
},
stream: function(data) {
if (!stream) {
throw new Error('Module not found: stream');
}
return new Readable(this, data, this.buffer);
return new Readable(this, data, this._options);
}
};
if (stream) {
Readable = function(template, data, buffer) {
Readable = function(template, data, options) {
Readable.$super.call(this);
this._t = template;
this._d = data;
this._buffer = buffer;
this._options = options;
this._rendered = false;
};
@ -210,7 +239,7 @@ if (stream) {
var template = this._t;
var data = this._d;
var out = asyncWriter.create(this, this._buffer ? { buffer: true } : null);
var out = asyncWriter.create(this, this._options);
template.render(data, out);
out.end();
}