Skip to content

Commit a4224e7

Browse files
committed
feat: release version 1.0.0 of @angular-extensions/lint-rules
0 parents  commit a4224e7

22 files changed

+4952
-0
lines changed

.circleci/config.yml

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
version: 2.1
2+
jobs:
3+
build:
4+
docker:
5+
- image: circleci/node:12
6+
steps:
7+
- checkout
8+
# Download and cache dependencies
9+
- restore_cache:
10+
keys:
11+
- v1-dependencies-{{ checksum "package-lock.json" }}
12+
# fallback to using the latest cache if no exact match is found
13+
- v1-dependencies-
14+
- run: npm install
15+
- save_cache:
16+
paths:
17+
- node_modules
18+
key: v1-dependencies-{{ checksum "package-lock.json" }}
19+
- run: npm run test

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/node_modules
2+
/.idea
3+
/*.iml
4+
/dist

README.md

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# @angular-extensions/lint-rules
2+
[https://github.com/angular-extensions/lint-rules](https://github.com/angular-extensions/lint-rules)
3+
4+
[![npm version](https://img.shields.io/npm/v/@angular-extensions/lint-rules.svg?style=flat-square)](https://www.npmjs.com/package/@angular-extensions/lint-rules)
5+
[![npm downloads total](https://img.shields.io/npm/dt/@angular-extensions/lint-rules.svg?style=flat-square)](https://www.npmjs.com/package/@angular-extensions/lint-rules)
6+
[![npm downloads monthly](https://img.shields.io/npm/dm/@angular-extensions/lint-rules.svg?style=flat-square)](https://www.npmjs.com/package/@angular-extensions/lint-rules)
7+
[![CircleCI](https://circleci.com/gh/bithost-gmbh/@angular-extensions/lint-rules.svg?style=svg)](https://circleci.com/gh/bithost-gmbh/@angular-extensions/lint-rules)
8+
9+
## Description
10+
This repository offers some [tslint](https://github.com/palantir/tslint) rules useful for angular projects, see [Rules](#Rules).
11+
12+
## Installation / Usage
13+
* Install the [@angular-extensions/lint-rules](https://www.npmjs.com/package/@angular-extensions/lint-rules) npm package:
14+
```
15+
npm install @angular-extensions/lint-rules --save-dev
16+
```
17+
* Add `@angular-extensions/lint-rules` to the `extensions` list in your `tslint.json`:
18+
```json
19+
{
20+
"extends": [
21+
"tslint:recommended",
22+
"@angular-extensions/lint-rules"
23+
]
24+
}
25+
```
26+
* Lint your project with
27+
```
28+
ng lint
29+
```
30+
31+
## Rules
32+
The package includes the following rules:
33+
34+
| Rule | Description | Details | Enabled by default? |
35+
| --- | --- | --- | --- |
36+
| `angular-call-super-lifecycle-method-in-extended-class` | Enforces the application to call parent lifecycle function e.g. `super.ngOnDestroy()` when using inheritance within an Angular component or directive. | [Details](#angular-call-super-lifecycle-method-in-extended-class) | yes |
37+
| `angular-rxjs-takeuntil-before-subscribe` | Enforces the application of the `takeUntil` operator when calling of `subscribe` within an Angular component or directive. | [Details](#angular-rxjs-takeuntil-before-subscribe) | yes |
38+
39+
### angular-call-super-lifecycle-method-in-extended-class
40+
This rule tries to avoid memory leaks and other problems in angular components and directives by ensuring that
41+
a [life-cycle method](https://angular.io/guide/lifecycle-hooks), e.g. `ngOnDestroy(){}`, overriding its parent implementation
42+
must call the parent implementation with `super.ngOnDestroy()`.
43+
44+
#### Example
45+
This should trigger an error:
46+
```typescript
47+
class MyClass {
48+
ngOnDestroy() {
49+
const a = 5;
50+
}
51+
}
52+
@Component({
53+
selector: 'app-my'
54+
})
55+
class MyComponent2 extends MyClass {
56+
57+
ngOnDestroy() {
58+
~~~~~~~~~~~ call to super.ngOnDestroy() is missing
59+
const b = 6;
60+
}
61+
}
62+
```
63+
while this should be fine:
64+
```typescript
65+
class MyClass {
66+
ngOnDestroy() {
67+
const a = 5;
68+
}
69+
}
70+
@Component({
71+
selector: 'app-my'
72+
})
73+
class MyComponent extends MyClass {
74+
75+
ngOnDestroy() {
76+
super.ngOnDestroy();
77+
const b = 6;
78+
}
79+
}
80+
81+
@Component({
82+
selector: 'app-my2'
83+
})
84+
class MyComponent2 {
85+
ngOnDestroy() {
86+
const b = 6;
87+
}
88+
}
89+
```
90+
91+
92+
### angular-rxjs-takeuntil-before-subscribe
93+
94+
This rule tries to avoid memory leaks in angular components and directives when calling `.subscribe()` without properly unsubscribing
95+
by enforcing the application of the `takeUntil(this.destroy$)` operator before the `.subscribe()`
96+
as well as before certain operators (`shareReplay` without `refCount: true`)
97+
and ensuring the component implements the `ngOnDestroy`
98+
method invoking `this.destroy$.next()` and `this.destroy$.complete()`.
99+
All classes with a `@Component` or `@Directive` decorator and all their parent classes will be checked.
100+
101+
#### Example
102+
This should trigger an error:
103+
```typescript
104+
@Component({
105+
selector: 'app-my',
106+
template: '<div>{{k$ | async}}</div>'
107+
})
108+
class MyComponent {
109+
~~~~~~~~~~~ component containing subscribe must implement the ngOnDestroy() method
110+
111+
112+
k$ = a.pipe(shareReplay(1));
113+
~~~~~~~~~~~~~~ the shareReplay operator used within a component must be preceded by takeUntil
114+
115+
someMethod() {
116+
const e = a.pipe(switchMap(_ => b)).subscribe();
117+
~~~~~~~~~ subscribe within a component must be preceded by takeUntil
118+
}
119+
}
120+
```
121+
122+
while this should be fine:
123+
```typescript
124+
@Component({
125+
selector: 'app-my',
126+
template: '<div>{{k$ | async}}</div>'
127+
})
128+
class MyComponent implements SomeInterface, OnDestroy {
129+
private destroy$: Subject<void> = new Subject<void>();
130+
131+
k$ = a.pipe(takeUntil(this.destroy$), shareReplay(1));
132+
133+
someMethod() {
134+
const e = a.pipe(switchMap(_ => b), takeUntil(this.destroy$)).subscribe();
135+
}
136+
137+
ngOnDestroy() {
138+
this.destroy$.next();
139+
this.destroy$.complete();
140+
}
141+
}
142+
```
143+
144+
145+
## Further reading
146+
* https://slides.com/estebangehring/angular-app-memory-leak
147+
* https://blog.angularindepth.com/the-best-way-to-unsubscribe-rxjs-observable-in-the-angular-applications-d8f9aa42f6a0
148+
* https://github.com/cartant/rxjs-tslint-rules/pull/107
149+
150+
## Contributors
151+
* Esteban Gehring (@macjohnny)
152+
* Lutz Bliska (@lbliska)
153+
154+
Note: this project is based on work in https://github.com/cartant/rxjs-tslint-rules/pull/107
155+
156+
## Development
157+
Clone the repository and install the dependencies with `npm install`.
158+
159+
Note: using the build artifacts with `npm link` does not work correctly,
160+
since there will be a mismatch between the typescript version used by the consumer code
161+
and the typescript version used by the lint rules code.
162+
To test the package in a project, run
163+
```
164+
npm run build
165+
cd dist
166+
npm install --production
167+
```
168+
Then copy the content of the `/dist` folder (including the `node_modules` folder) into `node_modules/@angular-extensions/lint-rules`
169+
in the consumer project.
170+
171+
### Publish
172+
To publish the package, run
173+
```
174+
npm run publish-package
175+
```

buildscripts/replace-config.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"from": "\"private\": true,",
3+
"to": "",
4+
"files": [
5+
"dist/package.json"
6+
]
7+
}

lint-rules.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"rulesDirectory": ["rules"],
3+
"rules": {
4+
"angular-rxjs-takeuntil-before-subscribe": { "severity": "error" },
5+
"angular-call-super-lifecycle-method-in-extended-class": { "severity": "error" }
6+
}
7+
}

0 commit comments

Comments
 (0)