Skip to content

Commit

Permalink
implements wix-incubator#175, better rt-scope syntax parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
nippur72 committed Jul 30, 2016
1 parent 46caeea commit 2654eba
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 17 deletions.
56 changes: 43 additions & 13 deletions src/reactTemplates.js
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,35 @@ function convertHtmlToReact(node, context) {
}
}

/**
* Parses the rt-scope attribute returning an array of parsed sections
*
* @param {String} scope The scope attribute to parse
* @returns {Array} an array of {expression,identifier}
* @throws {String} the part of the string that failed to parse
*/
function parseScopeSyntax(text) {
// in plain english, this regex scans for:
// any character + one or more space + "as" + one or more space + JavaScript identifier +
// zero or more space + semicolon or end of line + zero or more space
// it captures "any character" as the scope expression, "JavaScript identifier" as the identifier
const regex = RegExp('([\\s\\S]*?)(?: )+as(?: )+([$_a-zA-Z]+[$_a-zA-Z0-9]*)(?: )*(?:;|$)(?: )*', 'g');
const res = [];
do {
const idx = regex.lastIndex;
const match = regex.exec(text);
if (regex.lastIndex === idx || match === null) {
throw text.substr(idx);
}
if (match.index === regex.lastIndex) {
regex.lastIndex++;
}
res.push({expression: match[1].trim(), identifier: match[2]});
} while (regex.lastIndex < text.length);

return res;
}

function handleScopeAttribute(node, context, data) {
data.innerScope = {
scopeName: '',
Expand All @@ -426,25 +455,26 @@ function handleScopeAttribute(node, context, data) {

data.innerScope.outerMapping = _.zipObject(context.boundParams, context.boundParams);

_(node.attribs[scopeAttr]).split(';').invokeMap('trim').compact().forEach(scopePart => {
const scopeSubParts = _(scopePart).split(' as ').invokeMap('trim').value();
if (scopeSubParts.length < 2) {
throw RTCodeError.build(context, node, `invalid scope part '${scopePart}'`);
}
const alias = scopeSubParts[1];
const value = scopeSubParts[0];
validateJS(alias, node, context);
let scopes;
try {
scopes = parseScopeSyntax(node.attribs[scopeAttr]);
} catch (scopePart) {
throw RTCodeError.build(context, node, `invalid scope part '${scopePart}'`);
}

scopes.forEach(({expression, identifier}) => {
validateJS(identifier, 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.
if (!_.includes(context.boundParams, alias)) {
context.boundParams.push(alias);
if (!_.includes(context.boundParams, identifier)) {
context.boundParams.push(identifier);
}

data.innerScope.scopeName += _.upperFirst(alias);
data.innerScope.innerMapping[alias] = `var ${alias} = ${value};`;
validateJS(data.innerScope.innerMapping[alias], node, context);
data.innerScope.scopeName += _.upperFirst(identifier);
data.innerScope.innerMapping[identifier] = `var ${identifier} = ${expression};`;
validateJS(data.innerScope.innerMapping[identifier], node, context);
});
}

Expand Down
4 changes: 4 additions & 0 deletions test/data/scope-reserved-tokens.rt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div rt-scope="'as fast as possible' as message;';' as semicolon;'as' as as">
{message}{semicolon}{as}
</div>

2 changes: 2 additions & 0 deletions test/data/scope-reserved-tokens.rt.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<div>as fast as possible;as</div>

7 changes: 3 additions & 4 deletions test/src/rt.valid.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,13 @@ module.exports = {
'scope-evaluated-after-repeat2.rt',
'scope-evaluated-after-if.rt',
'scope-obj.rt',
'scope-reserved-tokens.rt',
'repeat-literal-collection.rt',
'include.rt'
];
t.plan(files.length);

files.forEach(check);

function check(testFile) {
files.forEach(testFile => {
const filename = path.join(dataPath, testFile);
const options = {
readFileSync: fsUtil.createRelativeReadFileSync(filename)
Expand All @@ -196,7 +195,7 @@ module.exports = {
console.log(testFile, e);
fs.writeFileSync(filename + '.code.js', code);
}
}
});
});
}
};

0 comments on commit 2654eba

Please sign in to comment.