Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow slashes in unquoted prop values (unless immediately followed by >) #112

Merged
merged 3 commits into from
Jul 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ It uses standard JavaScript [Tagged Templates] and works in [all modern browsers

⚛️ **< 500 bytes** when used with Preact _(thanks gzip 🌈)_

🥚 **< 420 byte** `htm/mini` version
🥚 **< 450 byte** `htm/mini` version

🏅 **0 bytes** if compiled using [babel-plugin-htm]

Expand Down
17 changes: 9 additions & 8 deletions src/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,23 @@ export const treeify = (built, fields) => {

export const evaluate = (h, built, fields, args) => {
for (let i = 1; i < built.length; i++) {
const field = built[i++];
const field = built[i];
const value = typeof field === 'number' ? fields[field] : field;
const type = built[++i];

if (built[i] === TAG_SET) {
if (type === TAG_SET) {
args[0] = value;
}
else if (built[i] === PROPS_ASSIGN) {
else if (type === PROPS_ASSIGN) {
args[1] = Object.assign(args[1] || {}, value);
}
else if (built[i] === PROP_SET) {
else if (type === PROP_SET) {
(args[1] = args[1] || {})[built[++i]] = value;
}
else if (built[i] === PROP_APPEND) {
else if (type === PROP_APPEND) {
args[1][built[++i]] += (value + '');
}
else if (built[i]) {
else if (type) {
// code === CHILD_RECURSE
args.push(h.apply(null, evaluate(h, value, fields, ['', null])));
}
Expand Down Expand Up @@ -178,7 +179,7 @@ export const build = function(statics) {
commit(i);
}

for (let j=0; j<statics[i].length; j++) {
for (let j=0; j<statics[i].length;j++) {
char = statics[i][j];

if (mode === MODE_TEXT) {
Expand Down Expand Up @@ -230,7 +231,7 @@ export const build = function(statics) {
propName = buffer;
buffer = '';
}
else if (char === '/') {
else if (char === '/' && (mode < MODE_PROP_SET || statics[i][j+1] === '>')) {
commit();
if (mode === MODE_TAGNAME) {
current = current[0];
Expand Down
13 changes: 13 additions & 0 deletions test/index.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ describe('htm', () => {
expect(html`<a href=${'foo'} />`).toEqual({ tag: 'a', props: { href: 'foo' }, children: [] });
});

test('slash in the middle of tag name or property name self-closes the element', () => {
expect(html`<ab/ba prop=value>`).toEqual({ tag: 'ab', props: null, children: [] });
expect(html`<abba pr/op=value>`).toEqual({ tag: 'abba', props: { pr: true }, children: [] });
});

test('slash in a property value does not self-closes the element, unless followed by >', () => {
expect(html`<abba prop=val/ue><//>`).toEqual({ tag: 'abba', props: { prop: 'val/ue' }, children: [] });
expect(html`<abba prop=value/>`).toEqual({ tag: 'abba', props: { prop: 'value' }, children: [] });
expect(html`<abba prop=value/ ><//>`).toEqual({ tag: 'abba', props: { prop: 'value/' }, children: [] });
});

test('two props with dynamic values', () => {
function onClick(e) { }
expect(html`<a href=${'foo'} onClick=${onClick} />`).toEqual({ tag: 'a', props: { href: 'foo', onClick }, children: [] });
Expand All @@ -86,6 +97,8 @@ describe('htm', () => {
expect(html`<a href="before${'foo'}after" />`).toEqual({ tag: 'a', props: { href: 'beforefooafter' }, children: [] });
expect(html`<a href=${1}${1} />`).toEqual({ tag: 'a', props: { href: '11' }, children: [] });
expect(html`<a href=${1}between${1} />`).toEqual({ tag: 'a', props: { href: '1between1' }, children: [] });
expect(html`<a href=/before/${'foo'}/after />`).toEqual({ tag: 'a', props: { href: '/before/foo/after' }, children: [] });
expect(html`<a href=/before/${'foo'}/>`).toEqual({ tag: 'a', props: { href: '/before/foo' }, children: [] });
});

test('spread props', () => {
Expand Down