close #62
This commit is contained in:
parent
6db983e7cc
commit
b3fd044344
|
@ -293,9 +293,8 @@ function generateProps(node, context) {
|
|||
res[1] = res.slice(1).join(':').trim();
|
||||
return res;
|
||||
}));
|
||||
var styleArray = [];
|
||||
_.forEach(styleParts, function (stylePart) {
|
||||
styleArray.push(stringUtils.convertToCamelCase(stylePart[0]) + ' : ' + convertText(node, context, stylePart[1].trim()));
|
||||
var styleArray = _.map(styleParts, function (stylePart) {
|
||||
return stringUtils.convertToCamelCase(stylePart[0]) + ' : ' + convertText(node, context, stylePart[1].trim());
|
||||
});
|
||||
props[propKey] = '{' + styleArray.join(',') + '}';
|
||||
} else if (propKey === classNameProp) {
|
||||
|
@ -362,6 +361,33 @@ function hasNonSimpleChildren(node) {
|
|||
});
|
||||
}
|
||||
|
||||
/*
|
||||
interface NodeConversionData {
|
||||
innerScopeData: InnerScopeData;
|
||||
repeatChildrenData: RepeatChildrenData;
|
||||
ifData: IfData;
|
||||
}
|
||||
|
||||
interface InnerScopeData {
|
||||
scopeName: string;
|
||||
// these are variables that were already in scope, unrelated to the ones declared in rt-inner-scope
|
||||
innerMapping: {[alias: string]: any};
|
||||
// these are variables declared in the rt-inner-scope attribute
|
||||
outerMapping: {[alias: string]: any};
|
||||
}
|
||||
|
||||
interface RepeatChildrenData {
|
||||
itemAlias: string;
|
||||
collectionExpression: string;
|
||||
binds: string[];
|
||||
fn();
|
||||
}
|
||||
|
||||
interface IfData {
|
||||
conditionExpression: string;
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param node
|
||||
* @param {Context} context
|
||||
|
@ -377,41 +403,9 @@ function convertHtmlToReact(node, context) {
|
|||
};
|
||||
|
||||
var data = {name: convertTagNameToConstructor(node.name, context)};
|
||||
if (node.attribs[scopeAttr]) {
|
||||
//data.scopeMapping = {};
|
||||
data.scopeName = '';
|
||||
|
||||
// these are variables that were already in scope, unrelated to the ones declared in rt-scope
|
||||
data.outerScopeMapping = {};
|
||||
_.forEach(context.boundParams, function (boundParam) {
|
||||
data.outerScopeMapping[boundParam] = boundParam;
|
||||
});
|
||||
|
||||
// these are variables declared in the rt-scope attribute
|
||||
data.innerScopeMapping = {};
|
||||
_.forEach(node.attribs[scopeAttr].split(';'), function (scopePart) {
|
||||
if (scopePart.trim().length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var scopeSubParts = scopePart.split(' as ');
|
||||
if (scopeSubParts.length < 2) {
|
||||
throw RTCodeError.build("invalid scope part '" + scopePart + "'", context, node);
|
||||
}
|
||||
var scopeName = scopeSubParts[1].trim();
|
||||
validateJS(scopeName, node, context);
|
||||
|
||||
// this adds both parameters to the list of parameters passed further down
|
||||
// the scope chain, as well as variables that are locally bound before any
|
||||
// function call, as with the ones we generate for rt-scope.
|
||||
stringUtils.addIfMissing(context.boundParams, scopeName);
|
||||
|
||||
data.scopeName += stringUtils.capitalize(scopeName);
|
||||
data.innerScopeMapping[scopeName] = scopeSubParts[0].trim();
|
||||
validateJS(data.innerScopeMapping[scopeName], node, context);
|
||||
});
|
||||
}
|
||||
|
||||
// Order matters. We need to add the item and itemIndex to context.boundParams before
|
||||
// the rt-scope directive is processed, lest they are not passed to the child scopes
|
||||
if (node.attribs[templateAttr]) {
|
||||
var arr = node.attribs[templateAttr].split(' in ');
|
||||
if (arr.length !== 2) {
|
||||
|
@ -424,6 +418,44 @@ function convertHtmlToReact(node, context) {
|
|||
stringUtils.addIfMissing(context.boundParams, data.item);
|
||||
stringUtils.addIfMissing(context.boundParams, data.item + 'Index');
|
||||
}
|
||||
|
||||
if (node.attribs[scopeAttr]) {
|
||||
data.innerScope = {
|
||||
scopeName: '',
|
||||
innerMapping: {},
|
||||
outerMapping: {}
|
||||
};
|
||||
|
||||
_.forEach(context.boundParams, function (boundParam) {
|
||||
data.innerScope.outerMapping[boundParam] = boundParam;
|
||||
});
|
||||
|
||||
//_(node.attribs[scopeAttr]).split(';').invoke('trim').compact().forEach().value()
|
||||
_.forEach(node.attribs[scopeAttr].split(';'), function (scopePart) {
|
||||
if (scopePart.trim().length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var scopeSubParts = scopePart.split(' as ');
|
||||
if (scopeSubParts.length < 2) {
|
||||
throw RTCodeError.build("invalid scope part '" + scopePart + "'", context, node);
|
||||
}
|
||||
var alias = scopeSubParts[1].trim();
|
||||
var value = scopeSubParts[0].trim();
|
||||
|
||||
validateJS(alias, node, context);
|
||||
|
||||
// this adds both parameters to the list of parameters passed further down
|
||||
// the scope chain, as well as variables that are locally bound before any
|
||||
// function call, as with the ones we generate for rt-scope.
|
||||
stringUtils.addIfMissing(context.boundParams, alias);
|
||||
|
||||
data.innerScope.scopeName += stringUtils.capitalize(alias);
|
||||
data.innerScope.innerMapping[alias] = value;
|
||||
validateJS(data.innerScope.innerMapping[alias], node, context);
|
||||
});
|
||||
}
|
||||
|
||||
data.props = generateProps(node, context);
|
||||
if (node.attribs[propsAttr]) {
|
||||
if (data.props === '{}') {
|
||||
|
@ -452,26 +484,25 @@ function convertHtmlToReact(node, context) {
|
|||
data.body = shouldUseCreateElement(context) ? simpleTagTemplateCreateElement(data) : simpleTagTemplate(data);
|
||||
}
|
||||
|
||||
if (node.attribs[scopeAttr]) {
|
||||
var scopeVarDeclarations = _.map(data.innerScope.innerMapping, function (v, k) { return 'var ' + k + ' = ' + v + ';'; }).join('\n');
|
||||
var functionBody = scopeVarDeclarations + 'return ' + data.body;
|
||||
var generatedFuncName = generateInjectedFunc(context, 'scope' + data.innerScope.scopeName, functionBody, _.keys(data.innerScope.outerMapping));
|
||||
data.body = generatedFuncName + '.apply(this, [' + _.values(data.innerScope.outerMapping).join(',') + '])';
|
||||
}
|
||||
|
||||
// Order matters here. Each rt-repeat iteration wraps over the rt-scope, so
|
||||
// the scope variables are evaluated in context of the current iteration.
|
||||
if (node.attribs[templateAttr]) {
|
||||
data.repeatFunction = generateInjectedFunc(context, 'repeat' + stringUtils.capitalize(data.item), 'return ' + data.body);
|
||||
data.repeatBinds = ['this'].concat(_.reject(context.boundParams, function (param) {
|
||||
return param === data.item || param === data.item + 'Index';
|
||||
return param === data.item || param === data.item + 'Index' || data.innerScope && param in data.innerScope.innerMapping;
|
||||
}));
|
||||
data.body = repeatTemplate(data);
|
||||
}
|
||||
if (node.attribs[ifAttr]) {
|
||||
data.body = ifTemplate(data);
|
||||
}
|
||||
if (node.attribs[scopeAttr]) {
|
||||
var scopeVarDeclarations = _.reduce(data.innerScopeMapping, function (acc, rightHandSide, leftHandSide) {
|
||||
var declaration = 'var ' + leftHandSide + ' = ' + rightHandSide + ';';
|
||||
return acc + declaration;
|
||||
}, '');
|
||||
var functionBody = scopeVarDeclarations + 'return ' + data.body;
|
||||
var generatedFuncName = generateInjectedFunc(context, 'scope' + data.scopeName, functionBody, _.keys(data.outerScopeMapping));
|
||||
data.body = generatedFuncName + '.apply(this, [' + _.values(data.outerScopeMapping).join(',') + '])';
|
||||
}
|
||||
return data.body;
|
||||
} else if (node.type === 'comment') {
|
||||
return commentTemplate(node);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<div>
|
||||
<div rt-repeat="pirate in [{first: 'Jack', last: 'Sparrow', photo: {url: 'jack.jpg'}}, {first: 'Unknown', last: 'Pirate'}]">
|
||||
<h1>{pirate.first} {pirate.last}</h1>
|
||||
<div rt-if="pirate.photo" rt-scope="pirate.photo.url as url">
|
||||
<img src="{url}"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1 @@
|
|||
<div><div><h1>Jack Sparrow</h1><div><img src="jack.jpg"/></div></div><div><h1>Unknown Pirate</h1></div></div>
|
|
@ -0,0 +1,5 @@
|
|||
<div>
|
||||
<div rt-repeat="pirate in [{name: 'Jack Sparrow', profile: {age: 99}}, {name: 'Someone Else', profile: {age: 98}}]" rt-scope="pirate.profile as profile" key="p-{pirateIndex}">
|
||||
{pirateIndex}-{pirate.name}-{profile.age}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1 @@
|
|||
<div><div>0-Jack Sparrow-99</div><div>1-Someone Else-98</div></div>
|
|
@ -0,0 +1,9 @@
|
|||
<div>
|
||||
<div rt-scope="15 as fifteen; 10 as ten">
|
||||
<div rt-repeat="number in [111]">
|
||||
<div rt-repeat="pirate in [{name: 'Jack Sparrow', profile: {age: 99}}, {name: 'Someone Else', profile: {age: 98}}]" rt-scope="pirate.profile as profile; 20 as twenty">
|
||||
{number}-{numberIndex}-{fifteen}-{ten}-{pirateIndex}-{pirate.name}-{profile.age}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1 @@
|
|||
<div><div><div><div>111-0-15-10-0-Jack Sparrow-99</div><div>111-0-15-10-1-Someone Else-98</div></div></div></div>
|
|
@ -2,6 +2,9 @@
|
|||
var test = require('tape');
|
||||
var reactTemplates = require('../../src/reactTemplates');
|
||||
var context = require('../../src/context');
|
||||
var util = require('./util');
|
||||
var readFileNormalized = util.readFileNormalized;
|
||||
var compareAndWrite = util.compareAndWrite;
|
||||
var fs = require('fs');
|
||||
var _ = require('lodash');
|
||||
var path = require('path');
|
||||
|
@ -10,14 +13,6 @@ var cheerio = require('cheerio');
|
|||
var RTCodeError = reactTemplates.RTCodeError;
|
||||
var dataPath = path.resolve(__dirname, '..', 'data');
|
||||
|
||||
/**
|
||||
* @param {string} filename
|
||||
* @return {string}
|
||||
*/
|
||||
function readFileNormalized(filename) {
|
||||
return fs.readFileSync(filename).toString().replace(/\r/g, '').trim();
|
||||
}
|
||||
|
||||
var invalidFiles = [
|
||||
{file: 'invalid-scope.rt', issue: new RTCodeError("invalid scope part 'a in a in a'", 0, 35, 1, 1)},
|
||||
{file: 'invalid-html.rt', issue: new RTCodeError('Document should have a root element', -1, -1, -1, -1)},
|
||||
|
@ -38,7 +33,7 @@ test('invalid tests', function (t) {
|
|||
|
||||
function check(testFile) {
|
||||
var filename = path.join(dataPath, testFile.file);
|
||||
var html = readFileNormalized(filename);
|
||||
var html = util.readFileNormalized(filename);
|
||||
var error = null;
|
||||
try {
|
||||
reactTemplates.convertTemplateToReact(html);
|
||||
|
@ -71,7 +66,7 @@ test('invalid tests json', function (t) {
|
|||
function check(testFile) {
|
||||
context.clear();
|
||||
var filename = path.join(dataPath, testFile.file);
|
||||
var options = {format: 'json'};
|
||||
var options = {format: 'json', force: true};
|
||||
cli.handleSingleFile(options, filename);
|
||||
t.deepEqual(normalizeError(context.getMessages()[0]), errorEqualMessage(testFile.issue, filename), 'Expect cli to produce valid output messages');
|
||||
}
|
||||
|
@ -160,22 +155,6 @@ test('conversion test - native', function (t) {
|
|||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {*} t
|
||||
* @param {string} actual
|
||||
* @param {string} expected
|
||||
* @param {string} filename
|
||||
* @return {boolean} whether actual is equal to expected
|
||||
*/
|
||||
function compareAndWrite(t, actual, expected, filename) {
|
||||
t.equal(actual, expected);
|
||||
if (actual !== expected) {
|
||||
fs.writeFileSync(filename + '.actual.js', actual);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
test('convert div with all module types', function (t) {
|
||||
var files = [
|
||||
{source: 'div.rt', expected: 'div.rt.commonjs.js', options: {modules: 'commonjs'}},
|
||||
|
@ -212,20 +191,11 @@ test('convert jsrt and test source results', function (t) {
|
|||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {string} html
|
||||
* @return {string}
|
||||
*/
|
||||
function normalizeHtml(html) {
|
||||
return cheerio.load(html, {normalizeWhitespace: true}).html()
|
||||
.replace(/\>\s+/mg, '>')
|
||||
.replace(/\s+\</mg, '<')
|
||||
.replace(/\>\s+\</mg, '><');
|
||||
}
|
||||
|
||||
test('html tests', function (t) {
|
||||
var files = ['scope.rt', 'scope-trailing-semicolon.rt', 'scope-variable-references.rt', 'lambda.rt', 'eval.rt', 'props.rt', 'custom-element.rt', 'style.rt', 'concat.rt',
|
||||
'js-in-attr.rt', 'props-class.rt', 'rt-class.rt', 'className.rt'];
|
||||
'js-in-attr.rt', 'props-class.rt', 'rt-class.rt', 'className.rt',
|
||||
'scope-evaluated-after-repeat.rt', 'scope-evaluated-after-repeat2.rt', 'scope-evaluated-after-if.rt'
|
||||
];
|
||||
t.plan(files.length);
|
||||
|
||||
files.forEach(check);
|
||||
|
@ -251,8 +221,8 @@ test('html tests', function (t) {
|
|||
render: eval(code) //eslint-disable-line no-eval
|
||||
}));
|
||||
var actual = React.renderToStaticMarkup(comp());
|
||||
actual = normalizeHtml(actual);
|
||||
expected = normalizeHtml(expected);
|
||||
actual = util.normalizeHtml(actual);
|
||||
expected = util.normalizeHtml(expected);
|
||||
var equal = compareAndWrite(t, actual, expected, filename);
|
||||
if (!equal) {
|
||||
fs.writeFileSync(filename + '.code.js', code);
|
||||
|
|
Loading…
Reference in New Issue