diff --git a/README.md b/README.md index 81129527c6..6d9d1d44a1 100644 --- a/README.md +++ b/README.md @@ -189,15 +189,16 @@ rules in templates can be disabled with eslint directives with mustache or html ### Best Practices -| Name | Description | 💼 | 🔧 | 💡 | -| :----------------------------------------------------------------------------------------------------- | :-------------------------------------------------------- | :- | :- | :- | -| [template-builtin-component-arguments](docs/rules/template-builtin-component-arguments.md) | disallow setting certain attributes on builtin components | | | | -| [template-no-action-modifiers](docs/rules/template-no-action-modifiers.md) | disallow usage of {{action}} modifiers | | | | -| [template-no-arguments-for-html-elements](docs/rules/template-no-arguments-for-html-elements.md) | disallow @arguments on HTML elements | | | | -| [template-no-array-prototype-extensions](docs/rules/template-no-array-prototype-extensions.md) | disallow usage of Ember Array prototype extensions | | | | -| [template-no-block-params-for-html-elements](docs/rules/template-no-block-params-for-html-elements.md) | disallow block params on HTML elements | | | | -| [template-no-debugger](docs/rules/template-no-debugger.md) | disallow {{debugger}} in templates | | | | -| [template-no-log](docs/rules/template-no-log.md) | disallow {{log}} in templates | | | | +| Name                                       | Description | 💼 | 🔧 | 💡 | +| :----------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- | :- | :- | :- | +| [template-builtin-component-arguments](docs/rules/template-builtin-component-arguments.md) | disallow setting certain attributes on builtin components | | | | +| [template-no-action-modifiers](docs/rules/template-no-action-modifiers.md) | disallow usage of {{action}} modifiers | | | | +| [template-no-arguments-for-html-elements](docs/rules/template-no-arguments-for-html-elements.md) | disallow @arguments on HTML elements | | | | +| [template-no-array-prototype-extensions](docs/rules/template-no-array-prototype-extensions.md) | disallow usage of Ember Array prototype extensions | | | | +| [template-no-block-params-for-html-elements](docs/rules/template-no-block-params-for-html-elements.md) | disallow block params on HTML elements | | | | +| [template-no-capital-arguments](docs/rules/template-no-capital-arguments.md) | disallow capital arguments (use lowercase @arg instead of @Arg) | | | | +| [template-no-debugger](docs/rules/template-no-debugger.md) | disallow {{debugger}} in templates | | | | +| [template-no-log](docs/rules/template-no-log.md) | disallow {{log}} in templates | | | | ### Components diff --git a/docs/rules/template-no-capital-arguments.md b/docs/rules/template-no-capital-arguments.md new file mode 100644 index 0000000000..e2048ec247 --- /dev/null +++ b/docs/rules/template-no-capital-arguments.md @@ -0,0 +1,43 @@ +# ember/template-no-capital-arguments + + + +Disallow capital letters in argument names. Use lowercase argument names (e.g., `@arg` instead of `@Arg`). + +## Rule Details + +This rule enforces the convention that argument names should start with lowercase letters. + +## Examples + +Examples of **incorrect** code for this rule: + +```gjs + +``` + +```gjs + +``` + +Examples of **correct** code for this rule: + +```gjs + +``` + +```gjs + +``` + +## References + +- [Ember Style Guide](https://github.com/ember-cli/ember-styleguide) diff --git a/lib/rules/template-no-capital-arguments.js b/lib/rules/template-no-capital-arguments.js new file mode 100644 index 0000000000..fe93bbb03d --- /dev/null +++ b/lib/rules/template-no-capital-arguments.js @@ -0,0 +1,47 @@ +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: 'disallow capital arguments (use lowercase @arg instead of @Arg)', + category: 'Best Practices', + recommended: false, + url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-capital-arguments.md', + }, + fixable: null, + schema: [], + messages: { + noCapitalArguments: + 'Argument names should start with lowercase. Use @{{lowercase}} instead of @{{name}}.', + }, + strictGjs: true, + strictGts: true, + }, + + create(context) { + function checkPath(node, path) { + if (!path || !path.head) { + return; + } + + const name = path.head.name || path.head; + if (typeof name === 'string' && name.startsWith('@') && /^@[A-Z]/.test(name)) { + const lowercase = name.charAt(0) + name.charAt(1).toLowerCase() + name.slice(2); + context.report({ + node, + messageId: 'noCapitalArguments', + data: { + name, + lowercase, + }, + }); + } + } + + return { + GlimmerPathExpression(node) { + checkPath(node, node); + }, + }; + }, +}; diff --git a/tests/lib/rules/template-no-capital-arguments.js b/tests/lib/rules/template-no-capital-arguments.js new file mode 100644 index 0000000000..9886b5f01d --- /dev/null +++ b/tests/lib/rules/template-no-capital-arguments.js @@ -0,0 +1,73 @@ +const { RuleTester } = require('eslint'); +const rule = require('../../../lib/rules/template-no-capital-arguments'); + +const ruleTester = new RuleTester({ + parser: require.resolve('ember-eslint-parser'), + parserOptions: { ecmaVersion: 2022, sourceType: 'module' }, +}); + +ruleTester.run('template-no-capital-arguments', rule, { + valid: [ + { + filename: 'my-component.gjs', + code: ` + import Component from '@glimmer/component'; + export default class MyComponent extends Component { + + } + `, + output: null, + }, + { + filename: 'my-component.gjs', + code: ` + import Component from '@glimmer/component'; + export default class MyComponent extends Component { + + } + `, + output: null, + }, + ], + + invalid: [ + { + filename: 'my-component.gjs', + code: ` + import Component from '@glimmer/component'; + export default class MyComponent extends Component { + + } + `, + output: null, + errors: [ + { + messageId: 'noCapitalArguments', + }, + ], + }, + { + filename: 'my-component.gjs', + code: ` + import Component from '@glimmer/component'; + export default class MyComponent extends Component { + + } + `, + output: null, + errors: [ + { + messageId: 'noCapitalArguments', + }, + ], + }, + ], +});