absolute/angular-one-feature-per-file
Disallow defining more than one Angular feature class (@Component, @Directive, @Pipe, @Injectable, @NgModule) per file.
#Rule Details
Mirrors the Angular Style Guide’s Single Responsibility / "Rule of One": every Angular feature gets its own file. The rule counts classes carrying a class-level feature decorator — @Component, @Directive, @Pipe, @Injectable, or @NgModule. The first one in a file is fine; every additional feature class is reported.
Member decorators such as @Input, @Output, and @HostListener are not features and never count, so a fully decorated component with many inputs is still a single feature. Undecorated helper classes are ignored entirely, so you can keep small private classes next to the feature they support.
Decorators are matched by their local name, so an aliased import (import { Component as NgComponent }) is not recognized. Spec and Storybook files routinely declare stub or host classes beside the subject under test — disable the rule for those globs with an override.
#Examples
@Component({ selector: 'app-card', template: '' })
class CardComponent {}
@Injectable({ providedIn: 'root' })
class CardService {} // flagged: second feature class in the file@Pipe({ name: 'currency' })
class CurrencyPipe {}
@Directive({ selector: '[appHighlight]' })
class HighlightDirective {} // flaggedOne feature class — member decorators are fine
@Component({ selector: 'app-card', template: '' })
export class CardComponent {
@Input() title = '';
@Output() select = new EventEmitter<string>();
@HostListener('click') onClick() {}
}Plain helper classes are ignored
@Component({ selector: 'app-card', template: '' })
export class CardComponent {}
// Undecorated helpers can live alongside it
class CardLayout {}
class CardData {}#Configuration
Enable this rule in your ESLint configuration:
// eslint.config.js
export default [
{
rules: {
'absolute/angular-one-feature-per-file': 'error'
}
},
{
// Specs and stories legitimately co-locate stub classes
files: ['**/*.spec.ts', '**/*.stories.ts'],
rules: {
'absolute/angular-one-feature-per-file': 'off'
}
}
];#Related Rules
#When Not To Use It
If you intentionally co-locate several small features in one file — for example test doubles, Storybook host components, or a tightly coupled directive plus its module — turn the rule off for those files with an ESLint override rather than disabling it project-wide.