From 2654eba854ecaeb06b26e40a952403e0dd2b1659 Mon Sep 17 00:00:00 2001 From: Antonino Porcino Date: Sat, 30 Jul 2016 15:46:43 +0200 Subject: [PATCH] implements #175, better rt-scope syntax parsing --- src/reactTemplates.js | 56 +++++++++++++++++++------ test/data/scope-reserved-tokens.rt | 4 ++ test/data/scope-reserved-tokens.rt.html | 2 + test/src/rt.valid.spec.js | 7 ++-- 4 files changed, 52 insertions(+), 17 deletions(-) create mode 100644 test/data/scope-reserved-tokens.rt create mode 100644 test/data/scope-reserved-tokens.rt.html diff --git a/src/reactTemplates.js b/src/reactTemplates.js index 6772d6fb..3ac44465 100644 --- a/src/reactTemplates.js +++ b/src/reactTemplates.js @@ -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: '', @@ -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); }); } diff --git a/test/data/scope-reserved-tokens.rt b/test/data/scope-reserved-tokens.rt new file mode 100644 index 00000000..83d0266f --- /dev/null +++ b/test/data/scope-reserved-tokens.rt @@ -0,0 +1,4 @@ +
+ {message}{semicolon}{as} +
+ diff --git a/test/data/scope-reserved-tokens.rt.html b/test/data/scope-reserved-tokens.rt.html new file mode 100644 index 00000000..e984be6e --- /dev/null +++ b/test/data/scope-reserved-tokens.rt.html @@ -0,0 +1,2 @@ +
as fast as possible;as
+ diff --git a/test/src/rt.valid.spec.js b/test/src/rt.valid.spec.js index 4f5a4416..123fd492 100644 --- a/test/src/rt.valid.spec.js +++ b/test/src/rt.valid.spec.js @@ -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) @@ -196,7 +195,7 @@ module.exports = { console.log(testFile, e); fs.writeFileSync(filename + '.code.js', code); } - } + }); }); } };