Overview
I released @rdlabo/eslint-plugin-rules π
I build apps with Ionic and Angular, and I packaged the ESLint rules I needed along the way into a plugin. Some of the features honestly made my work dramatically more efficient. On a large application, migration work that would normally take two or three days finished in an instant. Hooray!
Usage
- Assumes
@angular/core >= 15. It should work on 14, but not on 13. - You need the
@angular-eslintpackages applied to your project. If you have not done that yet, install them using this guide. - If you use
prettier, keep it up to date. An oldprettierversion throws an error when youinjectan object with a generic type parameter.
Installation
% npm install @rdlabo/eslint-plugin-rules --save-dev
Configuration
Configure .eslintrc* as follows. This shows my recommended setup, but of course you can skip any rules you do not want.
{
...
+ "plugins": ["@rdlabo/rules"],
"overrides": [
{
"files": [
"*.ts"
],
...
"rules": {
+ "@rdlabo/rules/deny-constructor-di": "error",
+ "@rdlabo/rules/import-inject-object": "error",
}
},
{
"files": [
"*.html"
],
"rules": {
+ "@rdlabo/rules/deny-element": [
+ "error",
+ {
+ "elements": [
+ "ion-modal",
+ "ion-popover",
+ "ion-toast",
+ "ion-alert",
+ "ion-loading",
+ "ion-picker",
+ "ion-action-sheet"
+ ]
+ }
+ ]
]
}
}
]
}
Rule reference
@rdlabo/rules/deny-constructor-di
This rule helps you migrate to the inject function introduced in Angular 14. Use it when moving away from DI in constructor. Both styles work today, but after reading the article below you will want to migrate!
https://zenn.dev/lacolaco/articles/why-inject-function-wins
Before migration: DI in constructor triggers an error.
@Component({
selector: 'app-confirm',
templateUrl: './confirm.page.html',
styleUrls: ['./confirm.page.scss'],
})
export class SigninPage {
constructor(public platform: Platform) {} // Error: Do not use constructor DI.
}
After migration: use the inject function.
@Component({
selector: 'app-confirm',
templateUrl: './confirm.page.html',
styleUrls: ['./confirm.page.scss'],
})
export class SigninPage {
public readonly platform = inject(Platform); // OK
constructor() {}
}
This rule supports auto-fix, so npm run lint -- --fix completes the migration automatically. For the example above, it generates a diff like this.
@Component({
selector: 'app-confirm',
templateUrl: './confirm.page.html',
styleUrls: ['./confirm.page.scss'],
})
export class SigninPage {
- constructor(public platform: Platform) {}
+ public readonly platform = inject(Platform); // OK
+
+ constructor() {}
}
@Component({
selector: 'app-confirm',
templateUrl: './confirm.page.html',
styleUrls: ['./confirm.page.scss'],
})
export class SigninPage {
- constructor(
- public platform: Platform,
- private store: Store<IApp>,
- private navCtrl: NavController,
- public helper: HelperService,
- ) {}
+ public readonly platform = inject(Platform);
+ private readonly store = inject(Store<IApp>);
+ private readonly navCtrl = inject(NavController);
+ public readonly helper = inject(HelperService);
+
+ constructor() {}
}
On a large project that means more than 100 files and thousands of DI sitesβfixing that by hand is not realistic. You could die trying. Let automation do the work!
@rdlabo/rules/import-inject-object
If you use the inject function without importing it, you get an error. I split this out because generating the inject import in the auto-fix for @rdlabo/rules/deny-constructor-di was hard. npm run lint -- --fix adds the import automatically.
+ import { inject } from '@angular/core';
@Component({
selector: 'app-confirm',
templateUrl: './confirm.page.html',
styleUrls: ['./confirm.page.scss'],
})
export class SigninPage {
public readonly platform = inject(Platform);
}
- import { Component } from '@angular/core';
+ import { Component, inject } from '@angular/core';
@Component({
selector: 'app-confirm',
templateUrl: './confirm.page.html',
styleUrls: ['./confirm.page.scss'],
})
export class SigninPage {
public readonly platform = inject(Platform);
}
@rdlabo/rules/deny-element
Since Ionic 6, inline Modals and ActionSheets are supported. On projects I work on, we standardize on creating them through Controllers, so I added this rule to avoid accidentally creating them inline. You can also use it to ban other specific HTML elements.
{
"rules": {
"@rdlabo/rules/deny-element": [
"error",
{
"elements": ["ion-modal"]
}
]
}
}
<ion-modal></ion-modal><!-- This causes an error -->
Summary
In day-to-day development, "try hard" and "be careful" inevitably wear you down. Turning expectations into rules and automating them reduces the burden on developers. Please give it a try. And if it saves you real time, buy me a drink sometime (lol)