mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
Fixes #36 Deprecated invokeBody in favor of renderBody
This commit is contained in:
parent
6a2f603ea9
commit
79aa92254f
34
README.md
34
README.md
@ -1383,13 +1383,13 @@ exports.render = function(input, out) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If, and only if, a tag has nested content, then a special `invokeBody` method will be added to the `input` object. If a renderer wants to render the nested body content then it must call the `invokeBody` method. For example:
|
If, and only if, a tag has nested content, then a special `renderBody` method will be added to the `input` object. If a renderer wants to render the nested body content then it must call the `renderBody` method. For example:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
exports.render = function(input, out) {
|
exports.render = function(input, out) {
|
||||||
out.write('BEFORE BODY');
|
out.write('BEFORE BODY');
|
||||||
if (input.invokeBody) {
|
if (input.renderBody) {
|
||||||
input.invokeBody();
|
input.renderBody(out);
|
||||||
}
|
}
|
||||||
out.write('AFTER BODY');
|
out.write('AFTER BODY');
|
||||||
}
|
}
|
||||||
@ -1531,12 +1531,12 @@ Marko supports this by leveraging JavaScript closures in the compiled output. A
|
|||||||
"tags": {
|
"tags": {
|
||||||
"ui-tabs": {
|
"ui-tabs": {
|
||||||
"renderer": "./tabs-tag",
|
"renderer": "./tabs-tag",
|
||||||
"var": "tabs"
|
"body-function": "getTabs(__tabsHelper)"
|
||||||
},
|
},
|
||||||
"ui-tab": {
|
"ui-tab": {
|
||||||
"renderer": "./tab-tag",
|
"renderer": "./tab-tag",
|
||||||
"import-var": {
|
"import-var": {
|
||||||
"tabs": "tabs"
|
"tabs": "__tabsHelper"
|
||||||
},
|
},
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"title": "string"
|
"title": "string"
|
||||||
@ -1557,15 +1557,21 @@ var templatePath = require.resolve('./template.marko');
|
|||||||
var template = require('marko').load(templatePath);
|
var template = require('marko').load(templatePath);
|
||||||
|
|
||||||
exports.render = function(input, out) {
|
exports.render = function(input, out) {
|
||||||
var nestedTabs = [];
|
var nestedTabs;
|
||||||
|
|
||||||
|
if (input.getTabs) {
|
||||||
|
nestedTabs = [];
|
||||||
|
// Invoke the body function to discover nested <ui-tab> tags
|
||||||
|
input.getTabs({ // Invoke the body with the scoped "tabs" variable
|
||||||
|
addTab: function(tab) {
|
||||||
|
tab.id = tab.id || ("tab" + tabs.length);
|
||||||
|
nestedTabs.push(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
nestedTabs = input.tabs || [];
|
||||||
|
}
|
||||||
|
|
||||||
// Invoke the body function to discover nested <ui-tab> tags
|
|
||||||
input.invokeBody({ // Invoke the body with the scoped "tabs" variable
|
|
||||||
addTab: function(tab) {
|
|
||||||
tab.id = tab.id || ("tab" + tabs.length);
|
|
||||||
nestedTabs.push(tab);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Now render the markup for the tabs:
|
// Now render the markup for the tabs:
|
||||||
template.render({
|
template.render({
|
||||||
@ -1596,7 +1602,7 @@ _components/tabs/template.marko:_
|
|||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div id="${tab.id}" class="tab-pane" for="tab in data.tabs">
|
<div id="${tab.id}" class="tab-pane" for="tab in data.tabs">
|
||||||
<invoke function="tab.invokeBody()"/>
|
<invoke function="tab.renderBody(out)"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -134,7 +134,7 @@ CodeWriter.prototype = {
|
|||||||
thisObj = arguments[2];
|
thisObj = arguments[2];
|
||||||
}
|
}
|
||||||
this.incIndent(delta);
|
this.incIndent(delta);
|
||||||
func.call(thisObj);
|
func.call(thisObj, this);
|
||||||
this.decIndent(delta);
|
this.decIndent(delta);
|
||||||
} else if (typeof arguments[0] === 'string') {
|
} else if (typeof arguments[0] === 'string') {
|
||||||
this.code(this._indent + arguments[0]);
|
this.code(this._indent + arguments[0]);
|
||||||
@ -264,8 +264,8 @@ TemplateBuilder.prototype = {
|
|||||||
var newWriter = new CodeWriter(this.concatWrites, oldWriter.indentStr());
|
var newWriter = new CodeWriter(this.concatWrites, oldWriter.indentStr());
|
||||||
try {
|
try {
|
||||||
this.writer = newWriter;
|
this.writer = newWriter;
|
||||||
func.call(thisObj);
|
var value = func.call(thisObj);
|
||||||
return newWriter.getOutput();
|
return value == null ? newWriter.getOutput() : value;
|
||||||
} finally {
|
} finally {
|
||||||
this.writer = oldWriter;
|
this.writer = oldWriter;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,6 +88,7 @@ Taglib.Tag = makeClass({
|
|||||||
this.nestedVariables = {};
|
this.nestedVariables = {};
|
||||||
this.importedVariables = {};
|
this.importedVariables = {};
|
||||||
this.patternAttributes = [];
|
this.patternAttributes = [];
|
||||||
|
this.bodyFunction = null;
|
||||||
},
|
},
|
||||||
inheritFrom: function (superTag) {
|
inheritFrom: function (superTag) {
|
||||||
var subTag = this;
|
var subTag = this;
|
||||||
@ -182,6 +183,12 @@ Taglib.Tag = makeClass({
|
|||||||
var key = transformer.path;
|
var key = transformer.path;
|
||||||
transformer.taglibId = this.taglibId;
|
transformer.taglibId = this.taglibId;
|
||||||
this.transformers[key] = transformer;
|
this.transformers[key] = transformer;
|
||||||
|
},
|
||||||
|
setBodyFunction: function(name, params) {
|
||||||
|
this.bodyFunction = {
|
||||||
|
name: name,
|
||||||
|
params: params
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,6 @@ try {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var ok = require('assert').ok;
|
var ok = require('assert').ok;
|
||||||
var nodePath = require('path');
|
var nodePath = require('path');
|
||||||
var Taglib = require('./Taglib');
|
var Taglib = require('./Taglib');
|
||||||
@ -18,6 +17,9 @@ var tagDefFromCode = require('./tag-def-from-code');
|
|||||||
var resolve = require('../util/resolve'); // NOTE: different implementation for browser
|
var resolve = require('../util/resolve'); // NOTE: different implementation for browser
|
||||||
var propertyHandlers = require('property-handlers');
|
var propertyHandlers = require('property-handlers');
|
||||||
|
|
||||||
|
var safeVarName = /^[A-Za-z_$][A-Za-z0-9_]*$/;
|
||||||
|
var bodyFunctionRegExp = /^([A-Za-z_$][A-Za-z0-9_]*)(?:\(([^)]*)\))?$/;
|
||||||
|
|
||||||
function exists(path) {
|
function exists(path) {
|
||||||
try {
|
try {
|
||||||
require.resolve(path);
|
require.resolve(path);
|
||||||
@ -192,6 +194,29 @@ function buildTag(tagObject, path, taglib, dirname) {
|
|||||||
name: value
|
name: value
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
bodyFunction: function(value) {
|
||||||
|
var parts = bodyFunctionRegExp.exec(value);
|
||||||
|
if (!parts) {
|
||||||
|
throw new Error('Invalid value of "' + value + '" for "body-function". Expected value to be of the following form: <function-name>([param1, param2, ...])');
|
||||||
|
}
|
||||||
|
|
||||||
|
var functionName = parts[1];
|
||||||
|
var params = parts[2];
|
||||||
|
if (params) {
|
||||||
|
params = params.trim().split(/\s*,\s*/);
|
||||||
|
for (var i=0; i<params.length; i++) {
|
||||||
|
if (params[i].length === 0) {
|
||||||
|
throw new Error('Invalid parameters for body-function with value of "' + value + '"');
|
||||||
|
} else if (!safeVarName.test(params[i])) {
|
||||||
|
throw new Error('Invalid parameter name of "' + params[i] + '" for body-function with value of "' + value + '"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
params = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
tag.setBodyFunction(functionName, params);
|
||||||
|
},
|
||||||
vars: function(value) {
|
vars: function(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
value.forEach(function(v, i) {
|
value.forEach(function(v, i) {
|
||||||
|
|||||||
@ -25,8 +25,8 @@
|
|||||||
"char-props": "~0.1.5",
|
"char-props": "~0.1.5",
|
||||||
"events": "^1.0.2",
|
"events": "^1.0.2",
|
||||||
"htmlparser2": "^3.7.2",
|
"htmlparser2": "^3.7.2",
|
||||||
"marko-async": "^1.2.12",
|
"marko-async": "^2.0.0",
|
||||||
"marko-layout": "^1.1.0",
|
"marko-layout": "^2.0.0",
|
||||||
"minimatch": "^0.2.14",
|
"minimatch": "^0.2.14",
|
||||||
"property-handlers": "^1.0.0",
|
"property-handlers": "^1.0.0",
|
||||||
"raptor-args": "^1.0.0",
|
"raptor-args": "^1.0.0",
|
||||||
|
|||||||
@ -7,6 +7,8 @@ var attrs = require('raptor-util/attrs');
|
|||||||
var forEach = require('raptor-util/forEach');
|
var forEach = require('raptor-util/forEach');
|
||||||
var markoRegExp = /\.marko(.xml|.html)?$/;
|
var markoRegExp = /\.marko(.xml|.html)?$/;
|
||||||
var req = require;
|
var req = require;
|
||||||
|
var arrayFromArguments = require('raptor-util/arrayFromArguments');
|
||||||
|
var logger = require('raptor-logging').logger(module);
|
||||||
|
|
||||||
function notEmpty(o) {
|
function notEmpty(o) {
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
@ -20,6 +22,8 @@ function notEmpty(o) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var WARNED_INVOKE_BODY = 0;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
s: function(str) {
|
s: function(str) {
|
||||||
return (str == null) ? '' : str;
|
return (str == null) ? '' : str;
|
||||||
@ -132,13 +136,27 @@ module.exports = {
|
|||||||
/**
|
/**
|
||||||
* Invoke a tag handler render function
|
* Invoke a tag handler render function
|
||||||
*/
|
*/
|
||||||
t: function (out, renderFunc, input, body) {
|
t: function (out, renderFunc, input, body, hasOutParam) {
|
||||||
if (!input) {
|
if (!input) {
|
||||||
input = {};
|
input = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body) {
|
if (body) {
|
||||||
input.invokeBody = body;
|
input.renderBody = body;
|
||||||
|
input.invokeBody = function() {
|
||||||
|
if (!WARNED_INVOKE_BODY) {
|
||||||
|
WARNED_INVOKE_BODY = 1;
|
||||||
|
logger.warn('invokeBody(...) deprecated. Use renderBody(out) instead.', new Error().stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasOutParam) {
|
||||||
|
var args = arrayFromArguments(arguments);
|
||||||
|
args.unshift(out);
|
||||||
|
body.apply(this, args);
|
||||||
|
} else {
|
||||||
|
body.apply(this, arguments);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
renderFunc(input, out);
|
renderFunc(input, out);
|
||||||
|
|||||||
@ -34,7 +34,15 @@ function getPropsStr(props, template) {
|
|||||||
template.indent(function () {
|
template.indent(function () {
|
||||||
forEachEntry(props, function (name, value) {
|
forEachEntry(props, function (name, value) {
|
||||||
if (typeof value === 'function') {
|
if (typeof value === 'function') {
|
||||||
value = value();
|
value = template.captureCode(function() {
|
||||||
|
return value(template);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
throw new Error('Invalid value for property "' + name + '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
value = template.makeExpression(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (template.isExpression(value)) {
|
if (template.isExpression(value)) {
|
||||||
@ -51,7 +59,6 @@ function getPropsStr(props, template) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if (propsArray.length) {
|
if (propsArray.length) {
|
||||||
return '{\n' + propsArray.join(',\n') + '\n' + template.indentStr() + '}';
|
return '{\n' + propsArray.join(',\n') + '\n' + template.indentStr() + '}';
|
||||||
} else {
|
} else {
|
||||||
@ -102,16 +109,24 @@ TagHandlerNode.prototype = {
|
|||||||
},
|
},
|
||||||
doGenerateCode: function (template) {
|
doGenerateCode: function (template) {
|
||||||
template.addStaticVar('__renderer', '__helpers.r');
|
template.addStaticVar('__renderer', '__helpers.r');
|
||||||
|
var _this = this;
|
||||||
var rendererPath = template.getRequirePath(this.tag.renderer); // Resolve a path to the renderer relative to the directory of the template
|
var rendererPath = template.getRequirePath(this.tag.renderer); // Resolve a path to the renderer relative to the directory of the template
|
||||||
var handlerVar = addHandlerVar(template, rendererPath);
|
var handlerVar = addHandlerVar(template, rendererPath);
|
||||||
var tagHelperVar = template.addStaticVar('__tag', '__helpers.t');
|
var tagHelperVar = template.addStaticVar('__tag', '__helpers.t');
|
||||||
|
var bodyFunction = this.tag.bodyFunction;
|
||||||
|
|
||||||
this.tag.forEachImportedVariable(function (importedVariable) {
|
this.tag.forEachImportedVariable(function (importedVariable) {
|
||||||
this.setProperty(importedVariable.targetProperty, template.makeExpression(importedVariable.expression));
|
this.setProperty(importedVariable.targetProperty, template.makeExpression(importedVariable.expression));
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
var _this = this;
|
if (bodyFunction) {
|
||||||
|
this.setProperty(bodyFunction.name, function(template) {
|
||||||
|
template.code('function(' + bodyFunction.params + ') {\n').indent(function () {
|
||||||
|
_this.generateCodeForChildren(template);
|
||||||
|
}).indent().code('}');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var variableNames = [];
|
var variableNames = [];
|
||||||
_this.tag.forEachVariable(function (nestedVar) {
|
_this.tag.forEachVariable(function (nestedVar) {
|
||||||
var varName;
|
var varName;
|
||||||
@ -165,14 +180,32 @@ TagHandlerNode.prototype = {
|
|||||||
|
|
||||||
template.code(getPropsStr(_this.getProperties(), template));
|
template.code(getPropsStr(_this.getProperties(), template));
|
||||||
}
|
}
|
||||||
if (_this.hasChildren()) {
|
if (_this.hasChildren() && !_this.tag.bodyFunction) {
|
||||||
var bodyParams = [];
|
var bodyParams = [];
|
||||||
|
var hasOutParam = false;
|
||||||
|
|
||||||
variableNames.forEach(function (varName) {
|
variableNames.forEach(function (varName) {
|
||||||
|
if (varName === 'out') {
|
||||||
|
hasOutParam = true;
|
||||||
|
}
|
||||||
bodyParams.push(varName);
|
bodyParams.push(varName);
|
||||||
});
|
});
|
||||||
template.code(',\n').line('function(' + bodyParams.join(',') + ') {').indent(function () {
|
|
||||||
|
var params;
|
||||||
|
|
||||||
|
if (hasOutParam) {
|
||||||
|
params = bodyParams.join(',');
|
||||||
|
} else {
|
||||||
|
params = 'out' + (bodyParams.length ? ',' + bodyParams.join(',') : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
template.code(',\n').line('function(' + params + ') {').indent(function () {
|
||||||
_this.generateCodeForChildren(template);
|
_this.generateCodeForChildren(template);
|
||||||
}).indent().code('}');
|
}).indent().code('}');
|
||||||
|
|
||||||
|
if (hasOutParam) {
|
||||||
|
template.code(',\n').code(template.indentStr() + '1');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -60,7 +60,7 @@ function testRender(path, data, done, options) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('marko/marko' , function() {
|
describe('marko/render' , function() {
|
||||||
|
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
// for (var k in require.cache) {
|
// for (var k in require.cache) {
|
||||||
|
|||||||
@ -4,12 +4,26 @@
|
|||||||
<test-tab title="Tab 1">
|
<test-tab title="Tab 1">
|
||||||
Tab 1 content
|
Tab 1 content
|
||||||
</test-tab>
|
</test-tab>
|
||||||
|
|
||||||
<test-tab title="Tab 2">
|
<test-tab title="Tab 2">
|
||||||
Tab 2 content
|
Tab 2 content
|
||||||
</test-tab>
|
</test-tab>
|
||||||
|
|
||||||
<test-tab title="Tab 3" if="showConditionalTab">
|
<test-tab title="Tab 3" if="showConditionalTab">
|
||||||
Tab 3 content
|
Tab 3 content
|
||||||
</test-tab>
|
</test-tab>
|
||||||
</test-tabs>
|
</test-tabs>
|
||||||
|
|
||||||
|
<test-tabs-new>
|
||||||
|
<test-tab-new title="Tab 1">
|
||||||
|
Tab 1 content
|
||||||
|
</test-tab-new>
|
||||||
|
|
||||||
|
<test-tab-new title="Tab 2">
|
||||||
|
Tab 2 content
|
||||||
|
</test-tab-new>
|
||||||
|
|
||||||
|
<test-tab-new title="Tab 3" if="showConditionalTab">
|
||||||
|
Tab 3 content
|
||||||
|
</test-tab-new>
|
||||||
|
</test-tabs-new>
|
||||||
@ -1 +1 @@
|
|||||||
<div class="tabs"><ul class="nav nav-tabs"><li class="active"><a href="#tab0" data-toggle="tab">Tab 1</a></li><li><a href="#tab1" data-toggle="tab">Tab 2</a></li></ul><div class="tab-content"><div id="tab0" class="tab-pane active">Tab 1 content</div><div id="tab1" class="tab-pane">Tab 2 content</div></div></div>
|
<div class="tabs"><ul class="nav nav-tabs"><li class="active"><a href="#tab0" data-toggle="tab">Tab 1</a></li><li><a href="#tab1" data-toggle="tab">Tab 2</a></li></ul><div class="tab-content"><div id="tab0" class="tab-pane active">Tab 1 content</div><div id="tab1" class="tab-pane">Tab 2 content</div></div></div><div class="tabs"><ul class="nav nav-tabs"><li class="active"><a href="#tab0" data-toggle="tab">Tab 1</a></li><li><a href="#tab1" data-toggle="tab">Tab 2</a></li></ul><div class="tab-content"><div id="tab0" class="tab-pane active">Tab 1 content</div><div id="tab1" class="tab-pane">Tab 2 content</div></div></div>
|
||||||
@ -26,6 +26,19 @@
|
|||||||
"title": "string"
|
"title": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"test-tabs-new": {
|
||||||
|
"renderer": "./tabs-new-tag.js",
|
||||||
|
"body-function": "buildTabs(__tabsHelper)"
|
||||||
|
},
|
||||||
|
"test-tab-new": {
|
||||||
|
"renderer": "./tab-new-tag.js",
|
||||||
|
"import-var": {
|
||||||
|
"tabs": "__tabsHelper"
|
||||||
|
},
|
||||||
|
"attributes": {
|
||||||
|
"title": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
"test-dynamic-attributes": {
|
"test-dynamic-attributes": {
|
||||||
"renderer": "./dynamic-attributes-tag.js",
|
"renderer": "./dynamic-attributes-tag.js",
|
||||||
"attributes": {
|
"attributes": {
|
||||||
|
|||||||
4
test/test-project/tab-new-tag.js
Normal file
4
test/test-project/tab-new-tag.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
exports.render = function(input, out) {
|
||||||
|
var tabs = input.tabs;
|
||||||
|
tabs.addTab(input);
|
||||||
|
};
|
||||||
33
test/test-project/tabs-new-tag.js
Normal file
33
test/test-project/tabs-new-tag.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
var marko = require('../../');
|
||||||
|
|
||||||
|
exports.render = function(input, out) {
|
||||||
|
var tabs = [],
|
||||||
|
activeFound = false;
|
||||||
|
|
||||||
|
if (input.buildTabs) {
|
||||||
|
input.buildTabs({
|
||||||
|
addTab: function(tab) {
|
||||||
|
if (tab.active) {
|
||||||
|
tab.activeFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
tab.id = "tab" + tabs.length;
|
||||||
|
tabs.push(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!activeFound && tabs.length) {
|
||||||
|
tabs[0].active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
tabs.forEach(function(tab) {
|
||||||
|
tab.liClass = tab.active ? "active" : "";
|
||||||
|
tab.divClass = tab.active ? "tab-pane active" : "tab-pane";
|
||||||
|
});
|
||||||
|
|
||||||
|
marko.render(require.resolve('./tabs.marko'), {
|
||||||
|
tabs: tabs
|
||||||
|
}, out);
|
||||||
|
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user