Skip to content

Commit 5588e6a

Browse files
committed
Fix template-no-quoteless-attributes false positive on quoted values containing =
Master's detection uses an unanchored regex `/=\s*[^"'{]/` against the full attribute source text. For an attribute like `data-x="foo=bar"`, the regex matches the `=` between `foo` and `bar` (followed by `b`), incorrectly flagging the well-quoted attribute as quoteless. The "autofix" then replaces the attribute text with itself — a no-op fix that still surfaces the spurious error. Replaces both detection regexes with a direct first-character check on the value node's source text. `sourceCode.getText(node.value)` returns the value verbatim including its surrounding quotes when present (`"foo"` for `class="foo"`, `foo` for `class=foo`, `""` for the empty range of a valueless attribute), so a single character comparison is sufficient and unambiguous. Adds two regression tests (one in each parser suite) for the `data-x="foo=bar"` case. Both fail on master and pass on the branch. The valueless-attribute case (`<input disabled>`, `...attributes`) is already covered by existing valid tests and is preserved by the `valueSource.length === 0` guard.
1 parent c97528a commit 5588e6a

2 files changed

Lines changed: 19 additions & 18 deletions

File tree

lib/rules/template-no-quoteless-attributes.js

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,27 @@ module.exports = {
2121
},
2222
},
2323
create(context) {
24+
const sourceCode = context.sourceCode;
2425
return {
2526
GlimmerAttrNode(node) {
26-
// Check if attribute has text value without quotes
27-
if (node.value?.type === 'GlimmerTextNode' && !/^["']/.test(node.value.chars)) {
28-
const sourceCode = context.sourceCode;
29-
const attrText = sourceCode.getText(node);
27+
if (node.value?.type !== 'GlimmerTextNode') {
28+
return;
29+
}
3030

31-
// If value looks unquoted (no = or =value without quotes)
32-
if (/=\s*[^"'{]/.test(attrText)) {
33-
const type = node.name?.startsWith('@') ? 'Argument' : 'Attribute';
34-
context.report({
35-
node,
36-
messageId: 'missing',
37-
data: { type, name: node.name },
38-
fix(fixer) {
39-
const valueText = node.value.chars;
40-
const replacementText = `${node.name}="${valueText}"`;
41-
return fixer.replaceText(node, replacementText);
42-
},
43-
});
44-
}
31+
const valueSource = sourceCode.getText(node.value);
32+
if (valueSource.length === 0 || valueSource[0] === '"' || valueSource[0] === "'") {
33+
return;
4534
}
35+
36+
const type = node.name?.startsWith('@') ? 'Argument' : 'Attribute';
37+
context.report({
38+
node,
39+
messageId: 'missing',
40+
data: { type, name: node.name },
41+
fix(fixer) {
42+
return fixer.replaceText(node, `${node.name}="${node.value.chars}"`);
43+
},
44+
});
4645
},
4746
};
4847
},

tests/lib/rules/template-no-quoteless-attributes.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ ruleTester.run('template-no-quoteless-attributes', rule, {
1717
'<template><SomeThing ...attributes /></template>',
1818
'<template><div></div></template>',
1919
'<template><input disabled></template>',
20+
'<template><div data-x="foo=bar"></div></template>',
2021
],
2122
invalid: [
2223
{
@@ -57,6 +58,7 @@ hbsRuleTester.run('template-no-quoteless-attributes', rule, {
5758
'<SomeThing ...attributes />',
5859
'<div></div>',
5960
'<input disabled>',
61+
'<div data-x="foo=bar"></div>',
6062
],
6163
invalid: [
6264
{

0 commit comments

Comments
 (0)