Skip to content

Commit 1a32b61

Browse files
AndrewJakubowiczSteve Orvell
andauthored
[lit-html] Add DEV_MODE error if duplicate attribute bindings are encountered (#4523)
Co-authored-by: Steve Orvell <sorvell@google.com>
1 parent 8d3e305 commit 1a32b61

3 files changed

Lines changed: 46 additions & 0 deletions

File tree

.changeset/forty-schools-flash.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'lit-html': patch
3+
'lit': patch
4+
---
5+
6+
Add a DEV_MODE error to catch duplicate attribute bindings that otherwise create silent errors.

packages/lit-html/src/lit-html.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,28 @@ class Template {
10361036
}
10371037
nodeIndex++;
10381038
}
1039+
1040+
if (DEV_MODE) {
1041+
// If there was a duplicate attribute on a tag, then when the tag is
1042+
// parsed into an element the attribute gets de-duplicated. We can detect
1043+
// this mismatch if we haven't precisely consumed every attribute name
1044+
// when preparing the template. This works because `attrNames` is built
1045+
// from the template string and `attrNameIndex` comes from processing the
1046+
// resulting DOM.
1047+
if (attrNames.length !== attrNameIndex) {
1048+
throw new Error(
1049+
`Detected duplicate attribute bindings. This occurs if your template ` +
1050+
`has duplicate attributes on an element tag. For example ` +
1051+
`"<input ?disabled=\${true} ?disabled=\${false}>" contains a ` +
1052+
`duplicate "disabled" attribute. The error was detected in ` +
1053+
`the following template: \n` +
1054+
'`' +
1055+
strings.join('${...}') +
1056+
'`'
1057+
);
1058+
}
1059+
}
1060+
10391061
// We could set walker.currentNode to another node here to prevent a memory
10401062
// leak, but every time we prepare a template, we immediately render it
10411063
// and re-use the walker in new TemplateInstance._clone().

packages/lit-html/src/test/lit-html_test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2193,6 +2193,24 @@ suite('lit-html', () => {
21932193
);
21942194
});
21952195

2196+
skipTestIfCompiled('Duplicate attributes throw', () => {
2197+
assert.throws(() => {
2198+
render(
2199+
html`<input ?disabled=${true} ?disabled=${false} fooAttribute=${'potato'}>`,
2200+
container
2201+
);
2202+
}, `Detected duplicate attribute bindings. This occurs if your template has duplicate attributes on an element tag. For example "<input ?disabled=\${true} ?disabled=\${false}>" contains a duplicate "disabled" attribute. The error was detected in the following template: \n\`<input ?disabled=\${...} ?disabled=\${...} fooAttribute=\${...}>\``);
2203+
});
2204+
2205+
test('Matching attribute bindings across elements should not throw', () => {
2206+
assert.doesNotThrow(() => {
2207+
render(
2208+
html`<input ?disabled=${true}><input ?disabled=${false}>`,
2209+
container
2210+
);
2211+
});
2212+
});
2213+
21962214
test('Expressions inside nested templates throw in dev mode', () => {
21972215
// top level
21982216
assert.throws(() => {

0 commit comments

Comments
 (0)