From 776ee4272c269616bf86b3577aaf16482c618b72 Mon Sep 17 00:00:00 2001
From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com>
Date: Wed, 18 Mar 2026 18:27:45 -0400
Subject: [PATCH] Extract rule: template-splat-attributes-only
---
README.md | 1 +
docs/rules/template-splat-attributes-only.md | 32 +++++++++++
lib/rules/template-splat-attributes-only.js | 31 +++++++++++
.../rules/template-splat-attributes-only.js | 55 +++++++++++++++++++
4 files changed, 119 insertions(+)
create mode 100644 docs/rules/template-splat-attributes-only.md
create mode 100644 lib/rules/template-splat-attributes-only.js
create mode 100644 tests/lib/rules/template-splat-attributes-only.js
diff --git a/README.md b/README.md
index 0042af848f..fe88a45638 100644
--- a/README.md
+++ b/README.md
@@ -245,6 +245,7 @@ rules in templates can be disabled with eslint directives with mustache or html
| [template-no-obsolete-elements](docs/rules/template-no-obsolete-elements.md) | disallow obsolete HTML elements | | | |
| [template-no-outlet-outside-routes](docs/rules/template-no-outlet-outside-routes.md) | disallow {{outlet}} outside of route templates | | | |
| [template-no-page-title-component](docs/rules/template-no-page-title-component.md) | disallow usage of ember-page-title component | | | |
+| [template-splat-attributes-only](docs/rules/template-splat-attributes-only.md) | disallow ...spread other than ...attributes | | | |
| [template-style-concatenation](docs/rules/template-style-concatenation.md) | disallow string concatenation in inline styles | | | |
### Components
diff --git a/docs/rules/template-splat-attributes-only.md b/docs/rules/template-splat-attributes-only.md
new file mode 100644
index 0000000000..8cc7e7e856
--- /dev/null
+++ b/docs/rules/template-splat-attributes-only.md
@@ -0,0 +1,32 @@
+# ember/template-splat-attributes-only
+
+
+
+It is easy to introduce typos when typing out `...attributes` or to use e.g.
+`...arguments` instead. Unfortunately, that leads to a cryptic runtime error,
+but does not fail the build.
+
+This rule warns you when you use an attribute starting with `...` that is **not**
+`...attributes`.
+
+## Examples
+
+This rule **forbids** the following:
+
+```gjs
+
+```
+
+```gjs
+
+```
+
+This rule **allows** the following:
+
+```gjs
+
+```
+
+## References
+
+- [Ember 3.11 release](https://blog.emberjs.com/2019/07/15/ember-3-11-released.html)
diff --git a/lib/rules/template-splat-attributes-only.js b/lib/rules/template-splat-attributes-only.js
new file mode 100644
index 0000000000..8b54765578
--- /dev/null
+++ b/lib/rules/template-splat-attributes-only.js
@@ -0,0 +1,31 @@
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'disallow ...spread other than ...attributes',
+ category: 'Best Practices',
+ url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-splat-attributes-only.md',
+ templateMode: 'both',
+ },
+ schema: [],
+ messages: {
+ onlyAttributes: 'Only `...attributes` can be applied to elements',
+ },
+ originallyFrom: {
+ name: 'ember-template-lint',
+ rule: 'lib/rules/splat-attributes-only.js',
+ docs: 'docs/rule/splat-attributes-only.md',
+ tests: 'test/unit/rules/splat-attributes-only-test.js',
+ },
+ },
+ create(context) {
+ return {
+ GlimmerAttrNode(node) {
+ if (node.name?.startsWith('...') && node.name !== '...attributes') {
+ context.report({ node, messageId: 'onlyAttributes' });
+ }
+ },
+ };
+ },
+};
diff --git a/tests/lib/rules/template-splat-attributes-only.js b/tests/lib/rules/template-splat-attributes-only.js
new file mode 100644
index 0000000000..eb8958a3c2
--- /dev/null
+++ b/tests/lib/rules/template-splat-attributes-only.js
@@ -0,0 +1,55 @@
+const rule = require('../../../lib/rules/template-splat-attributes-only');
+const RuleTester = require('eslint').RuleTester;
+
+const ruleTester = new RuleTester({
+ parser: require.resolve('ember-eslint-parser'),
+ parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
+});
+
+ruleTester.run('template-splat-attributes-only', rule, {
+ valid: [
+ '',
+ '