From 2ffbfc501bf49da6c0a93fd431acd39189b6cc85 Mon Sep 17 00:00:00 2001 From: Tim Branyen Date: Sun, 16 Oct 2016 08:46:38 -0700 Subject: [PATCH] Fixes handling of interpolated attributes The isProp regular expression was insufficient as it only checked the preceding string, and if you had multiple values, the second preceding string would be a single space (attribute value delimiter). Now we run the regex starting from the end and working its way back to determine if we're in an attribute/property. --- dist/diffhtml-runtime.js | 23 +++++++++--------- dist/diffhtml.js | 26 +++++++++------------ lib/util/tagged-template.js | 23 +++++++++--------- test/unit/util/tagged-template.js | 39 +++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 39 deletions(-) create mode 100644 test/unit/util/tagged-template.js diff --git a/dist/diffhtml-runtime.js b/dist/diffhtml-runtime.js index fa3fe65a..56ecab37 100644 --- a/dist/diffhtml-runtime.js +++ b/dist/diffhtml-runtime.js @@ -2014,7 +2014,7 @@ var _escape2 = _interopRequireDefault(_escape); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -var isPropEx = /(=|'|")/; +var isPropEx = /(=|"|')[\w\s]+$/; var TOKEN = '__DIFFHTML__'; /** @@ -2060,7 +2060,7 @@ function html(strings) { } // Used to store markup and tokens. - var retVal = []; + var retVal = ''; // We filter the supplemental values by where they are used. Values are // either props or children. @@ -2074,30 +2074,29 @@ function html(strings) { // diffHTML HTML parser inline. They are passed as an additional argument // called supplemental. The following loop instruments the markup with tokens // that the parser then uses to assemble the correct tree. - strings.forEach(function (string) { + strings.forEach(function (string, i) { // Always add the string, we need it to parse the markup later. - retVal.push(string); + retVal += string; if (values.length) { + var nextString = strings[i + 1]; var value = nextValue(values); - var lastSegment = string.split(' ').pop(); - var lastCharacter = lastSegment.trim().slice(-1); - var isProp = Boolean(lastCharacter.match(isPropEx)); + var isProp = Boolean(retVal.match(isPropEx)); - if (isProp) { + if (isProp && ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' || typeof value == 'function')) { supplemental.props.push(value); - retVal.push(TOKEN); + retVal += TOKEN; } else if (Array.isArray(value) || (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') { supplemental.children.push(value); - retVal.push(TOKEN); + retVal += TOKEN; } else { - retVal.push(value); + retVal += value; } } }); // Parse the instrumented markup to get the Virtual Tree. - var childNodes = (0, _parser.parse)(retVal.join(''), supplemental).childNodes; + var childNodes = (0, _parser.parse)(retVal, supplemental).childNodes; // This makes it easier to work with a single element as a root, instead of // always return an array. diff --git a/dist/diffhtml.js b/dist/diffhtml.js index f975b677..ec760275 100644 --- a/dist/diffhtml.js +++ b/dist/diffhtml.js @@ -2126,7 +2126,6 @@ function parse(html, supplemental) { stack.push(currentParent); if (blockText.has(match[2])) { - console.log(blockText, match[2]); // A little test to find next or ... var closeMarkup = ''; var index = html.indexOf(closeMarkup, tagEx.lastIndex); @@ -2147,8 +2146,6 @@ function parse(html, supplemental) { if (match[1] || match[4] || selfClosing.has(match[2])) { if (match[2] !== currentParent.rawNodeName && options.strict) { - console.log(stack); - console.log(match, currentParent.rawNodeName); var nodeName = currentParent.rawNodeName; // Find a subset of the markup passed in to validate. @@ -2389,7 +2386,7 @@ var _escape2 = _interopRequireDefault(_escape); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -var isPropEx = /(=|'|")/; +var isPropEx = /(=|"|')[\w\s]+$/; var TOKEN = '__DIFFHTML__'; /** @@ -2435,7 +2432,7 @@ function html(strings) { } // Used to store markup and tokens. - var retVal = []; + var retVal = ''; // We filter the supplemental values by where they are used. Values are // either props or children. @@ -2449,30 +2446,29 @@ function html(strings) { // diffHTML HTML parser inline. They are passed as an additional argument // called supplemental. The following loop instruments the markup with tokens // that the parser then uses to assemble the correct tree. - strings.forEach(function (string) { + strings.forEach(function (string, i) { // Always add the string, we need it to parse the markup later. - retVal.push(string); + retVal += string; if (values.length) { + var nextString = strings[i + 1]; var value = nextValue(values); - var lastSegment = string.split(' ').pop(); - var lastCharacter = lastSegment.trim().slice(-1); - var isProp = Boolean(lastCharacter.match(isPropEx)); + var isProp = Boolean(retVal.match(isPropEx)); - if (isProp) { + if (isProp && ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' || typeof value == 'function')) { supplemental.props.push(value); - retVal.push(TOKEN); + retVal += TOKEN; } else if (Array.isArray(value) || (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') { supplemental.children.push(value); - retVal.push(TOKEN); + retVal += TOKEN; } else { - retVal.push(value); + retVal += value; } } }); // Parse the instrumented markup to get the Virtual Tree. - var childNodes = (0, _parser.parse)(retVal.join(''), supplemental).childNodes; + var childNodes = (0, _parser.parse)(retVal, supplemental).childNodes; // This makes it easier to work with a single element as a root, instead of // always return an array. diff --git a/lib/util/tagged-template.js b/lib/util/tagged-template.js index 5033c36a..4fc939f8 100644 --- a/lib/util/tagged-template.js +++ b/lib/util/tagged-template.js @@ -1,7 +1,7 @@ import { parse } from './parser'; import escape from './escape'; -const isPropEx = /(=|'|")/; +const isPropEx = /(=|"|')[\w\s]?$/; const TOKEN = '__DIFFHTML__'; /** @@ -43,7 +43,7 @@ export function html(strings, ...values) { } // Used to store markup and tokens. - const retVal = []; + let retVal = ''; // We filter the supplemental values by where they are used. Values are // either props or children. @@ -57,32 +57,31 @@ export function html(strings, ...values) { // diffHTML HTML parser inline. They are passed as an additional argument // called supplemental. The following loop instruments the markup with tokens // that the parser then uses to assemble the correct tree. - strings.forEach(string => { + strings.forEach((string, i) => { // Always add the string, we need it to parse the markup later. - retVal.push(string); + retVal += string; if (values.length) { + const nextString = strings[i + 1]; const value = nextValue(values); - const lastSegment = string.split(' ').pop(); - const lastCharacter = lastSegment.trim().slice(-1); - const isProp = Boolean(lastCharacter.match(isPropEx)); + const isProp = Boolean(retVal.match(isPropEx)); - if (isProp) { + if (isProp && (typeof value === 'object' || typeof value === 'function')) { supplemental.props.push(value); - retVal.push(TOKEN); + retVal += TOKEN; } else if (Array.isArray(value) || typeof value === 'object') { supplemental.children.push(value); - retVal.push(TOKEN); + retVal += TOKEN; } else { - retVal.push(value); + retVal += value; } } }); // Parse the instrumented markup to get the Virtual Tree. - const childNodes = parse(retVal.join(''), supplemental).childNodes; + const childNodes = parse(retVal, supplemental).childNodes; // This makes it easier to work with a single element as a root, instead of // always return an array. diff --git a/test/unit/util/tagged-template.js b/test/unit/util/tagged-template.js new file mode 100644 index 00000000..500f41be --- /dev/null +++ b/test/unit/util/tagged-template.js @@ -0,0 +1,39 @@ +import { html } from '../../../lib/util/tagged-template'; +import { cleanMemory } from '../../../lib/util/memory'; +import validateMemory from '../../util/validateMemory'; + +describe('Unit: Tagged template', function() { + afterEach(function() { + cleanMemory(); + validateMemory(); + }); + + it('can interpolate a single string value in an attribute', function() { + const foo = 'foo'; + + const multipleValues = html``; + + assert.equal(multipleValues.attributes[0].name, 'class'); + assert.equal(multipleValues.attributes[0].value, 'foo'); + }); + + it('can interpolate multiple string values in an attribute', function() { + const foo = 'foo'; + const bar = 'bar'; + + const multipleValues = html``; + + assert.equal(multipleValues.attributes[0].name, 'class'); + assert.equal(multipleValues.attributes[0].value, 'foo bar'); + }); + + it('can interpolate multiple type values in an attribute', function() { + const foo = 'foo'; + const bar = {}; + + const multipleValues = html``; + + assert.equal(multipleValues.attributes[0].name, 'class'); + assert.equal(multipleValues.attributes[0].value, 'foo __DIFFHTML__'); + }); +});