diff --git a/lib/rules/template-no-arguments-for-html-elements.js b/lib/rules/template-no-arguments-for-html-elements.js
index b9f4750139..4ea227f994 100644
--- a/lib/rules/template-no-arguments-for-html-elements.js
+++ b/lib/rules/template-no-arguments-for-html-elements.js
@@ -1,6 +1,10 @@
-/** @type {import('eslint').Rule.RuleModule} */
const htmlTags = require('html-tags');
+const svgTags = require('svg-tags');
+const { mathmlTagNames } = require('mathml-tag-names');
+
+const ELEMENT_TAGS = new Set([...htmlTags, ...svgTags, ...mathmlTagNames]);
+/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem',
@@ -16,27 +20,31 @@ module.exports = {
noArgumentsForHtmlElements:
'@arguments can only be used on components, not HTML elements. Use regular attributes instead.',
},
+ originallyFrom: {
+ name: 'ember-template-lint',
+ rule: 'lib/rules/no-arguments-for-html-elements.js',
+ docs: 'docs/rule/no-arguments-for-html-elements.md',
+ tests: 'test/unit/rules/no-arguments-for-html-elements-test.js',
+ },
},
create(context) {
const sourceCode = context.sourceCode;
- const HTML_ELEMENTS = new Set(htmlTags);
return {
GlimmerElementNode(node) {
- // Check if this is an HTML element (lowercase)
- if (!HTML_ELEMENTS.has(node.tag)) {
+ if (!ELEMENT_TAGS.has(node.tag)) {
return;
}
- // If the tag name is a variable in scope, it's being used as a component, not an HTML element
+ // A known HTML/SVG tag can still be a component if it's bound in scope
+ // (block param, import, local).
const scope = sourceCode.getScope(node.parent);
const isVariable = scope.references.some((ref) => ref.identifier === node.parts[0]);
if (isVariable) {
return;
}
- // Check for @arguments
for (const attr of node.attributes) {
if (attr.type === 'GlimmerAttrNode' && attr.name.startsWith('@')) {
context.report({
diff --git a/package.json b/package.json
index de20f9800e..488579b1d4 100644
--- a/package.json
+++ b/package.json
@@ -73,8 +73,10 @@
"html-tags": "^3.3.1",
"lodash.camelcase": "^4.3.0",
"lodash.kebabcase": "^4.1.1",
+ "mathml-tag-names": "^4.0.0",
"requireindex": "^1.2.0",
- "snake-case": "^3.0.3"
+ "snake-case": "^3.0.3",
+ "svg-tags": "^1.0.0"
},
"devDependencies": {
"@babel/core": "^7.25.9",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ea58f19332..301983850e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -41,12 +41,18 @@ importers:
lodash.kebabcase:
specifier: ^4.1.1
version: 4.1.1
+ mathml-tag-names:
+ specifier: ^4.0.0
+ version: 4.0.0
requireindex:
specifier: ^1.2.0
version: 1.2.0
snake-case:
specifier: ^3.0.3
version: 3.0.4
+ svg-tags:
+ specifier: ^1.0.0
+ version: 1.0.0
devDependencies:
'@babel/core':
specifier: ^7.25.9
diff --git a/tests/lib/rules/template-no-arguments-for-html-elements.js b/tests/lib/rules/template-no-arguments-for-html-elements.js
index 330ba683e6..d3bd2714e2 100644
--- a/tests/lib/rules/template-no-arguments-for-html-elements.js
+++ b/tests/lib/rules/template-no-arguments-for-html-elements.js
@@ -13,6 +13,13 @@ ruleTester.run('template-no-arguments-for-html-elements', rule, {
'',
'',
'',
+ // Custom elements aren't in the html-tags/svg-tags allowlists, so they're
+ // not flagged. Accepted false negative — web component namespace is open.
+ '',
+ // Namespaced/path component invocations aren't in the allowlists either.
+ '',
+ // Named blocks (colon-prefixed) aren't in the allowlists either.
+ '<:slot @item="x">content',
`let div = {{@greeting}}
@@ -54,5 +61,29 @@ ruleTester.run('template-no-arguments-for-html-elements', rule, {
},
],
},
+ {
+ // SVG element — in svg-tags allowlist.
+ code: '',
+ output: null,
+ errors: [
+ {
+ message:
+ '@arguments can only be used on components, not HTML elements. Use regular attributes instead.',
+ type: 'GlimmerAttrNode',
+ },
+ ],
+ },
+ {
+ // MathML element — in mathml-tag-names allowlist.
+ code: '',
+ output: null,
+ errors: [
+ {
+ message:
+ '@arguments can only be used on components, not HTML elements. Use regular attributes instead.',
+ type: 'GlimmerAttrNode',
+ },
+ ],
+ },
],
});