a post-component library for building user-interfaces on the web.

compiler: fix uppercase attribute bug (#136)

authored by tombl.dev and committed by

GitHub a5dee33a 3618816a

+24 -15
+16 -14
src/client/compiler.ts
··· 63 63 const walker = document.createTreeWalker(template_element.content, 129) 64 64 assert((NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT) === 129) 65 65 66 - // stop iterating once we've hit the last part, but if we're in dev mode, keep going to check for mistakes. 67 - while ((next_part < compiled._parts.length || __DEV__) && walker.nextNode()) { 66 + while (walker.nextNode()) { 68 67 const node = walker.currentNode 69 68 if (is_comment(node)) { 70 69 const match = DYNAMIC_WHOLE.exec(node.data) ··· 87 86 assert(is_element(node)) 88 87 assert(node instanceof HTMLElement || node instanceof SVGElement) 89 88 90 - const to_remove = [] 91 - for (let name of node.getAttributeNames()) { 89 + for (const name of node.getAttributeNames()) { 92 90 const value = node.getAttribute(name) 93 91 assert(value !== null) 94 92 95 93 let match = DYNAMIC_WHOLE.exec(name) 96 94 if (match !== null) { 97 95 // directive: 98 - to_remove.push(name) 96 + node.removeAttribute(name) 99 97 assert(value === '', `directives must not have values`) 100 98 patch(node, parseInt(match[1]), [PART_DIRECTIVE]) 101 99 } else { 102 100 // properties: 103 101 match = DYNAMIC_WHOLE.exec(value) 102 + const remapped_name = name.replace(NEEDS_UPPERCASING, match => match[1].toUpperCase()) 104 103 if (match !== null) { 105 - to_remove.push(name) 106 - if (FORCE_ATTRIBUTES.test(name)) { 107 - patch(node, parseInt(match[1]), [PART_ATTRIBUTE, name]) 104 + node.removeAttribute(name) 105 + if (FORCE_ATTRIBUTES.test(remapped_name)) { 106 + patch(node, parseInt(match[1]), [PART_ATTRIBUTE, remapped_name]) 108 107 } else { 109 - patch(node, parseInt(match[1]), [ 110 - PART_PROPERTY, 111 - name.replace(NEEDS_UPPERCASING, match => match[1].toUpperCase()), 112 - ]) 108 + patch(node, parseInt(match[1]), [PART_PROPERTY, remapped_name]) 113 109 } 110 + } else if (remapped_name !== name) { 111 + assert(!node.hasAttribute(remapped_name), `duplicate attribute ${remapped_name}`) 112 + node.setAttribute(remapped_name, value) 113 + node.removeAttribute(name) 114 114 } else { 115 - assert(!DYNAMIC_GLOBAL.test(value), `expected a whole dynamic value for ${name}, got a partial one`) 115 + assert( 116 + !DYNAMIC_GLOBAL.test(value), 117 + `expected a whole dynamic value for ${remapped_name}, got a partial one`, 118 + ) 116 119 } 117 120 } 118 121 } 119 - for (const name of to_remove) node.removeAttribute(name) 120 122 } 121 123 } 122 124
+8 -1
src/client/tests/attributes.test.ts
··· 1 1 import { html } from 'dhtml' 2 - import { assert, assert_eq, test } from '../../../scripts/test/test.ts' 2 + import { assert, assert_deep_eq, assert_eq, test } from '../../../scripts/test/test.ts' 3 3 import { setup } from './setup.ts' 4 4 5 5 test('regular attributes', () => { ··· 48 48 49 49 root.render(html`<span innerHTML=${innerHTML}></span>`) 50 50 assert_eq(el.querySelector('span')!.innerHTML, innerHTML) 51 + }) 52 + 53 + test('does not maintain the case of attributes', () => { 54 + const { root, el } = setup() 55 + 56 + root.render(html`<div theThing="hello"></div>`) 57 + assert_deep_eq(el.querySelector('div')!.getAttributeNames(), ['thething']) 51 58 }) 52 59 53 60 test('treats class/for specially', () => {