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: [ + '', + '', + + '', + '', + '', + ], + invalid: [ + { + code: '', + output: null, + errors: [{ messageId: 'onlyAttributes' }], + }, + + { + code: '', + output: null, + errors: [{ messageId: 'onlyAttributes' }], + }, + ], +}); + +const hbsRuleTester = new RuleTester({ + parser: require.resolve('ember-eslint-parser/hbs'), + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + }, +}); + +hbsRuleTester.run('template-splat-attributes-only', rule, { + valid: [ + '
', + '
', + '
', + '
', + ], + invalid: [ + { + code: '
', + output: null, + errors: [{ message: 'Only `...attributes` can be applied to elements' }], + }, + ], +});