Skip to content

Commit

Permalink
Fixes handling of interpolated attributes
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tbranyen committed Oct 16, 2016
1 parent e17977f commit 2ffbfc5
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 39 deletions.
23 changes: 11 additions & 12 deletions dist/diffhtml-runtime.js
Expand Up @@ -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__';

/**
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down
26 changes: 11 additions & 15 deletions dist/diffhtml.js
Expand Up @@ -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 </script> or </style> ...
var closeMarkup = '</' + match[2] + '>';
var index = html.indexOf(closeMarkup, tagEx.lastIndex);
Expand All @@ -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.
Expand Down Expand Up @@ -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__';

/**
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down
23 changes: 11 additions & 12 deletions 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__';

/**
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down
39 changes: 39 additions & 0 deletions 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`<span class="${foo}" />`;

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`<span class="${foo} ${bar}" />`;

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`<span class="${foo} ${bar}" />`;

assert.equal(multipleValues.attributes[0].name, 'class');
assert.equal(multipleValues.attributes[0].value, 'foo __DIFFHTML__');
});
});

0 comments on commit 2ffbfc5

Please sign in to comment.