ESLint plugin for Ember.js apps providing 97 specialized linting rules based on commonly known good practices in the Ember ecosystem
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Linting rules specific to Ember components, enforcing modern component patterns and preventing common anti-patterns. These rules help maintain consistency and best practices in Ember component development.
const eslintPluginEmber = require('eslint-plugin-ember');
// Access component rules
const componentRules = eslintPluginEmber.rules;ES Module:
import eslintPluginEmber from 'eslint-plugin-ember';
// Access component rules
const componentRules = eslintPluginEmber.rules;Rules governing action handling and event management in components.
/**
* Disallow the actions hash in components, controllers, and routes
* Encourages use of @action decorator instead
*/
'ember/no-actions-hash': ESLintRule;
/**
* Disallow on() calls in components
* Prevents direct event binding in component lifecycle
*/
'ember/no-on-calls-in-components': ESLintRule;
/**
* Require closure actions over sendAction
* Enforces modern action passing patterns
*/
'ember/closure-actions': ESLintRule;Usage Examples:
// ❌ Bad - actions hash (triggers no-actions-hash)
export default Component.extend({
actions: {
handleClick() {
// action logic
}
}
});
// ✅ Good - @action decorator
export default class MyComponent extends Component {
@action
handleClick() {
// action logic
}
}
// ❌ Bad - sendAction (triggers closure-actions)
this.sendAction('onUpdate', value);
// ✅ Good - closure action
this.args.onUpdate(value);Rules for managing component lifecycle hooks and preventing deprecated patterns.
/**
* Disallow component lifecycle hooks that are deprecated
* Enforces modern lifecycle patterns
*/
'ember/no-component-lifecycle-hooks': ESLintRule;
/**
* Require super calls in lifecycle hooks
* Ensures proper inheritance chain execution
*/
'ember/require-super-in-lifecycle-hooks': ESLintRule;Usage Examples:
// ❌ Bad - deprecated lifecycle hook
export default Component.extend({
didInsertElement() {
// This hook is deprecated
}
});
// ✅ Good - modern patterns with modifiers
<template>
<div {{did-insert this.setup}}></div>
</template>
// ❌ Bad - missing super call
export default class extends Component {
willDestroy() {
this.cleanup();
// Missing super call
}
}
// ✅ Good - proper super call
export default class extends Component {
willDestroy() {
super.willDestroy(...arguments);
this.cleanup();
}
}Rules governing component properties and attribute handling.
/**
* Disallow this.attrs in components
* Enforces args usage over deprecated attrs
*/
'ember/no-attrs-in-components': ESLintRule;
/**
* Disallow attrs snapshot usage
* Prevents deprecated attrs.foo pattern
*/
'ember/no-attrs-snapshot': ESLintRule;
/**
* Disallow empty attrs objects
* Removes unnecessary empty attrs declarations
*/
'ember/no-empty-attrs': ESLintRule;Usage Examples:
// ❌ Bad - using this.attrs (triggers no-attrs-in-components)
export default Component.extend({
someProperty: computed('attrs.value', function() {
return this.attrs.value * 2;
})
});
// ✅ Good - using args
export default class MyComponent extends Component {
@computed('args.value')
get someProperty() {
return this.args.value * 2;
}
}
// ❌ Bad - empty attrs (triggers no-empty-attrs)
export default Component.extend({
attrs: {}
});
// ✅ Good - no attrs declaration needed
export default class MyComponent extends Component {
// No attrs needed
}Rules enforcing modern component patterns over classic Ember patterns.
/**
* Disallow classic component patterns
* Encourages native class components
*/
'ember/no-classic-components': ESLintRule;
/**
* Disallow classic classes in general
* Promotes native ES classes
*/
'ember/no-classic-classes': ESLintRule;
/**
* Disallow empty Glimmer component classes
* Removes unnecessary empty component files
*/
'ember/no-empty-glimmer-component-classes': ESLintRule;Usage Examples:
// ❌ Bad - classic component (triggers no-classic-components)
export default Component.extend({
tagName: 'div',
classNames: ['my-component']
});
// ✅ Good - modern Glimmer component
export default class MyComponent extends Component {
// Modern component patterns
}
// ❌ Bad - empty Glimmer component (triggers no-empty-glimmer-component-classes)
export default class MyComponent extends Component {
// Empty class with no purpose
}
// ✅ Good - component with actual functionality
export default class MyComponent extends Component {
@tracked isVisible = true;
@action
toggle() {
this.isVisible = !this.isVisible;
}
}Rules for organizing component properties and maintaining consistent structure.
/**
* Enforce order of properties in components
* Maintains consistent component structure
*/
'ember/order-in-components': ESLintRule;
/**
* Require tagless components
* Encourages modern component architecture
*/
'ember/require-tagless-components': ESLintRule;Usage Examples:
// ❌ Bad - incorrect property order (triggers order-in-components)
export default Component.extend({
actions: {},
someProperty: 'value',
init() {
this._super(...arguments);
},
classNames: ['my-component']
});
// ✅ Good - correct property order
export default Component.extend({
classNames: ['my-component'],
someProperty: 'value',
init() {
this._super(...arguments);
},
actions: {}
});
// ❌ Bad - component with wrapper element
export default Component.extend({
tagName: 'div'
});
// ✅ Good - tagless component
export default class MyComponent extends Component {
// No wrapper element needed
}Rules for component template handling and built-in component usage.
/**
* Disallow built-in form components
* Encourages custom form component patterns
*/
'ember/no-builtin-form-components': ESLintRule;Usage Examples:
// ❌ Bad - built-in form components (triggers no-builtin-form-components)
{{input value=this.name}}
{{textarea value=this.description}}
// ✅ Good - custom form components
<Input @value={{this.name}} @onInput={{this.updateName}} />
<Textarea @value={{this.description}} @onInput={{this.updateDescription}} />Rules for managing component state and preventing common pitfalls.
/**
* Disallow assignment of untracked properties used in tracking contexts
* Prevents reactivity issues
*/
'ember/no-assignment-of-untracked-properties-used-in-tracking-contexts': ESLintRule;
/**
* Disallow tracked properties derived from args
* Prevents unnecessary tracking overhead
*/
'ember/no-tracked-properties-from-args': ESLintRule;Rules for managing component decorators and modern class patterns.
/**
* Enforce using correct hooks for both classic and non-classic classes
* Ensures proper lifecycle hook usage in modern components
*/
'ember/classic-decorator-hooks': ESLintRule;
/**
* Disallow usage of classic APIs in classes not decorated with @classic
* Prevents mixing classic and modern patterns incorrectly
*/
'ember/classic-decorator-no-classic-methods': ESLintRule;Usage Examples:
// ❌ Bad - using classic hook in modern class (triggers classic-decorator-hooks)
export default class MyComponent extends Component {
didInsertElement() {
// Classic hook in modern class
}
}
// ✅ Good - using modern patterns
export default class MyComponent extends Component {
@action
setupElement(element) {
// Modern pattern with modifiers or actions
}
}
// ❌ Bad - classic API without @classic (triggers classic-decorator-no-classic-methods)
export default class MyComponent extends Component {
init() {
this.set('value', 0); // Classic API in modern class
}
}
// ✅ Good - modern property assignment
export default class MyComponent extends Component {
@tracked value = 0; // Modern tracked property
}Rules for component template handling and render modifier usage.
/**
* Disallow importing from @ember/render-modifiers
* Prevents usage of deprecated render modifier imports
*/
'ember/no-at-ember-render-modifiers': ESLintRule;Usage Examples:
// ❌ Bad - deprecated render modifier import (triggers no-at-ember-render-modifiers)
import { modifier } from '@ember/render-modifiers';
// ✅ Good - use ember-modifier package instead
import { modifier } from 'ember-modifier';// ❌ Bad - untracked property in tracking context export default class MyComponent extends Component { someValue = 0; // Not tracked but used in template }
// ✅ Good - properly tracked property export default class MyComponent extends Component { @tracked someValue = 0; }
// ❌ Bad - tracked property from args (triggers no-tracked-properties-from-args) export default class MyComponent extends Component { @tracked derivedValue = this.args.value; }
// ✅ Good - computed property from args export default class MyComponent extends Component { get derivedValue() { return this.args.value; } }