diff --git a/README.md b/README.md index f87c310bb8..ac4e88593a 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,7 @@ rules in templates can be disabled with eslint directives with mustache or html | [template-no-chained-this](docs/rules/template-no-chained-this.md) | disallow redundant `this.this` in templates | | 🔧 | | | [template-no-debugger](docs/rules/template-no-debugger.md) | disallow {{debugger}} in templates | | | | | [template-no-element-event-actions](docs/rules/template-no-element-event-actions.md) | disallow element event actions (use {{on}} modifier instead) | | | | +| [template-no-inline-styles](docs/rules/template-no-inline-styles.md) | disallow inline styles | | | | | [template-no-input-placeholder](docs/rules/template-no-input-placeholder.md) | disallow placeholder attribute on input elements | | | | | [template-no-input-tagname](docs/rules/template-no-input-tagname.md) | disallow tagName attribute on {{input}} helper | | | | | [template-no-log](docs/rules/template-no-log.md) | disallow {{log}} in templates | | | | diff --git a/docs/rules/template-no-inline-styles.md b/docs/rules/template-no-inline-styles.md new file mode 100644 index 0000000000..cae7b58dc5 --- /dev/null +++ b/docs/rules/template-no-inline-styles.md @@ -0,0 +1,40 @@ +# ember/template-no-inline-styles + + + +Inline styles are not the best practice because they are hard to maintain and usually make the overall size of the project bigger. This rule forbids inline styles. Use CSS classes instead. + +## Examples + +This rule **forbids** the following: + +```gjs + +``` + +This rule **allows** the following: + +```gjs + +``` + +```gjs + +``` + +## Options + +| Name | Type | Default | Description | +| -------------------- | --------- | ------- | ------------------------------------------------------------------------------------- | +| `allowDynamicStyles` | `boolean` | `true` | When `true`, allows dynamic style values (e.g. `style={{...}}` or `style="{{...}}"`). | + +## Related Rules + +- [style-concatenation](style-concatenation.md) + +## References + +- [Deprecations/binding style attributes](https://emberjs.com/deprecations/v1.x/#toc_binding-style-attributes) diff --git a/lib/rules/template-no-inline-styles.js b/lib/rules/template-no-inline-styles.js new file mode 100644 index 0000000000..ec4b86f0a1 --- /dev/null +++ b/lib/rules/template-no-inline-styles.js @@ -0,0 +1,47 @@ +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: 'disallow inline styles', + category: 'Best Practices', + recommendedGjs: false, + recommendedGts: false, + url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-inline-styles.md', + }, + schema: [ + { + type: 'object', + properties: { + allowDynamicStyles: { type: 'boolean' }, + }, + additionalProperties: false, + }, + ], + messages: { noInlineStyles: 'Inline styles are not allowed' }, + }, + create(context) { + const options = context.options[0] || {}; + const allowDynamicStyles = + options.allowDynamicStyles === undefined ? true : options.allowDynamicStyles; + + return { + GlimmerElementNode(node) { + const styleAttr = node.attributes?.find((a) => a.name === 'style'); + if (!styleAttr) { + return; + } + + // If allowDynamicStyles is true, skip dynamic style values (MustacheStatement/ConcatStatement) + if (allowDynamicStyles) { + const valType = styleAttr.value?.type; + if (valType === 'GlimmerMustacheStatement' || valType === 'GlimmerConcatStatement') { + return; + } + } + + context.report({ node: styleAttr, messageId: 'noInlineStyles' }); + }, + }; + }, +}; diff --git a/tests/lib/rules/template-no-inline-styles.js b/tests/lib/rules/template-no-inline-styles.js new file mode 100644 index 0000000000..aee036ffed --- /dev/null +++ b/tests/lib/rules/template-no-inline-styles.js @@ -0,0 +1,25 @@ +const rule = require('../../../lib/rules/template-no-inline-styles'); +const RuleTester = require('eslint').RuleTester; + +const ruleTester = new RuleTester({ + parser: require.resolve('ember-eslint-parser'), + parserOptions: { ecmaVersion: 2022, sourceType: 'module' }, +}); +ruleTester.run('template-no-inline-styles', rule, { + valid: [ + '', + // Test cases ported from ember-template-lint + '', + '', + '', + '', + '', + ], + invalid: [ + { + code: '', + output: null, + errors: [{ messageId: 'noInlineStyles' }], + }, + ], +});