add support for function templates props

This commit is contained in:
Nir Natan 2015-08-09 23:37:33 +03:00
parent d565f5380f
commit 78799bfca7
10 changed files with 172 additions and 6 deletions

View File

@ -179,6 +179,51 @@ function generateInjectedFunc(context, namePrefix, body, params) {
return generatedFuncName;
}
function generateTemplateProps(node, context) {
var propTemplateDefinition = context.options.templates && context.options.templates[node.name];
var propertiesTemplates = _(node.children)
.map(function (child, index) {
var templateProp = null;
if (child.name === 'template' && child.attribs.prop) { // Generic explicit template tag
var childTemplate = _.find(context.options.templates, {prop: child.attribs.prop}) || {arguments: []};
templateProp = {
prop: child.attribs.prop,
arguments: child.attribs.arguments ? child.attribs.arguments.split(',') : childTemplate.arguments
};
} else if (propTemplateDefinition && propTemplateDefinition[child.name]) { // Implicit child template from configuration
templateProp = {
prop: propTemplateDefinition[child.name].prop,
arguments: child.attribs.arguments ? child.attribs.arguments.split(',') : propTemplateDefinition[child.name].arguments
};
}
if (templateProp) {
_.assign(templateProp, {childIndex: index, content: _.find(child.children, {type: 'tag'})});
}
return templateProp;
})
.compact()
.value();
return _.transform(propertiesTemplates, function (props, templateProp) {
var functionParams = _.values(context.boundParams).concat(templateProp.arguments);
var oldBoundParams = context.boundParams;
context.boundParams = context.boundParams.concat(templateProp.arguments);
var functionBody = 'return ' + convertHtmlToReact(templateProp.content, context);
context.boundParams = oldBoundParams;
var generatedFuncName = generateInjectedFunc(context, templateProp.prop, functionBody, functionParams);
var boundArguments = _.values(context.boundParams).join(',');
props[templateProp.prop] = generatedFuncName + '.bind(this' + (boundArguments.length ? ', ' + boundArguments : '') + ')';
// Remove the template child from the children definition.
node.children.splice(templateProp.childIndex, 1);
}, {});
}
/**
* @param node
* @param {Context} context
@ -237,6 +282,8 @@ function generateProps(node, context) {
}
});
_.assign(props, generateTemplateProps(node, context));
return '{' + _.map(props, function (val, key) {
return JSON.stringify(key) + ' : ' + val;
}).join(',') + '}';
@ -304,7 +351,7 @@ function convertHtmlToReact(node, context) {
_.each(context.boundParams, function (boundParam) {
data.outerScopeMapping[boundParam] = boundParam;
});
// these are variables declared in the rt-scope attribute
data.innerScopeMapping = {};
_.each(node.attribs[scopeAttr].split(';'), function (scopePart) {
@ -457,7 +504,7 @@ function convertTemplateToReact(html, options) {
function convertRT(html, reportContext, options) {
var rootNode = cheerio.load(html, {lowerCaseTags: false, lowerCaseAttributeNames: false, xmlMode: true, withStartIndices: true});
options = _.defaults({}, options, defaultOptions);
var defaultDefines = {};
defaultDefines[options.reactImportPath] = 'React';
defaultDefines[options.lodashImportPath] = '_';
@ -490,15 +537,23 @@ function convertRT(html, reportContext, options) {
throw RTCodeError.build('Document should have a single root element', context, rootNode.root()[0]);
}
var body = convertHtmlToReact(firstTag, context);
var requirePaths = _(defines).keys().map(function (reqName) { return '"' + reqName + '"'; }).value().join(',');
var requirePaths = _(defines).keys().map(function (reqName) {
return '"' + reqName + '"';
}).value().join(',');
var requireVars = _(defines).values().value().join(',');
var vars;
if (options.modules === 'typescript') {
vars = _(defines).map(function (reqVar, reqPath) { return 'import ' + reqVar + " = require('" + reqPath + "');"; }).join('\n');
vars = _(defines).map(function (reqVar, reqPath) {
return 'import ' + reqVar + " = require('" + reqPath + "');";
}).join('\n');
} else if (options.modules === 'es6') {
vars = _(defines).map(function (reqVar, reqPath) { return 'import ' + reqVar + " from '" + reqPath + "';"; }).join('\n');
vars = _(defines).map(function (reqVar, reqPath) {
return 'import ' + reqVar + " from '" + reqPath + "';";
}).join('\n');
} else {
vars = _(defines).map(function (reqVar, reqPath) { return 'var ' + reqVar + " = require('" + reqPath + "');"; }).join('\n');
vars = _(defines).map(function (reqVar, reqPath) {
return 'var ' + reqVar + " = require('" + reqPath + "');";
}).join('\n');
}
var data = {body: body, injectedFunctions: '', requireNames: requireVars, requirePaths: requirePaths, vars: vars, name: options.name};
data.injectedFunctions = context.injectedFunctions.join('\n');

View File

@ -0,0 +1,7 @@
<div>
<List data="{[1,2,3]}">
<Row>
<div>{rowData}</div>
</Row>
</List>
</div>

View File

@ -0,0 +1,19 @@
define([
'react/addons',
'lodash'
], function (React, _) {
'use strict';
function renderRow1(rowData) {
return React.createElement('div', {}, rowData);
}
return function () {
return React.createElement('div', {}, React.createElement(List, {
'data': [
1,
2,
3
],
'renderRow': renderRow1.bind(this)
}));
};
});

View File

@ -0,0 +1,5 @@
<div>
<template prop="templateProp" arguments="arg1">
<div>{arg1}</div>
</template>
</div>

View File

@ -0,0 +1,12 @@
define([
'react/addons',
'lodash'
], function (React, _) {
'use strict';
function templateProp1(arg1) {
return React.createElement('div', {}, arg1);
}
return function () {
return React.createElement('div', { 'templateProp': templateProp1.bind(this) });
};
});

View File

@ -0,0 +1,5 @@
<div rt-scope="'boten' as name">
<template prop="templateProp" arguments="arg1">
<div>Name: {name} {arg1}</div>
</template>
</div>

View File

@ -0,0 +1,16 @@
define([
'react/addons',
'lodash'
], function (React, _) {
'use strict';
function templateProp1(name, arg1) {
return React.createElement('div', {}, 'Name: ', name, ' ', arg1);
}
function scopeName2() {
var name = 'boten';
return React.createElement('div', { 'templateProp': templateProp1.bind(this, name) });
}
return function () {
return scopeName2.apply(this, []);
};
});

View File

@ -0,0 +1,10 @@
<div>
<template prop="templateProp" arguments="arg1">
<div>
<template prop="templateProp2" arguments="inner1, inner2">
<div>{arg1 + inner1 + inner2}</div>
</template>
<div>{arg1}</div>
</div>
</template>
</div>

View File

@ -0,0 +1,15 @@
define([
'react/addons',
'lodash'
], function (React, _) {
'use strict';
function templateProp21(arg1, inner1, inner2) {
return React.createElement('div', {}, arg1 + inner1 + inner2);
}
function templateProp2(arg1) {
return React.createElement('div', { 'templateProp2': templateProp21.bind(this, arg1) }, React.createElement('div', {}, arg1));
}
return function () {
return React.createElement('div', { 'templateProp': templateProp2.bind(this) });
};
});

View File

@ -114,6 +114,28 @@ test('conversion test', function (t) {
}
});
test('prop template conversion test', function (t) {
var options = {
templates: {
List: {
Row: {prop: 'renderRow', arguments: ['rowData']}
}
}
};
var files = ['propTemplates/simpleTemplate.rt', 'propTemplates/templateInScope.rt', 'propTemplates/implicitTemplate.rt', 'propTemplates/twoTemplates.rt'];
t.plan(files.length);
files.forEach(check);
function check(testFile) {
var filename = path.join(dataPath, testFile);
var html = readFileNormalized(filename);
var expected = readFileNormalized(filename + '.js');
var actual = reactTemplates.convertTemplateToReact(html, options).replace(/\r/g, '').trim();
compareAndWrite(t, actual, expected, filename);
}
});
/**
* @param {*} t
* @param {string} actual