Skip to content

feat: add rxjs-prefer-angular-takeuntil-before-subscribe rule #107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ yarn-error.log
/dist
/node_modules
/temp
/.idea
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ The package includes the following rules (none of which are enabled by default):
| `rxjs-no-unsafe-takeuntil` | Disallows the application of operators after `takeUntil`. Operators placed after `takeUntil` can effect [subscription leaks](https://medium.com/@cartant/rxjs-avoiding-takeuntil-leaks-fb5182d047ef). | [See below](#rxjs-no-unsafe-takeuntil) |
| `rxjs-no-unused-add` | Disallows the importation of patched observables or operators that are not used in the module. | None |
| `rxjs-no-wholesale` | Disallows the wholesale importation of `rxjs` or `rxjs/Rx`. | None |
| `rxjs-prefer-angular-takeuntil-before-subscribe` | Enforces the application of the `takeUntil` operator when calling of `subscribe` within an Angular component. | [See below](#rxjs-prefer-angular-takeuntil-before-subscribe) |
| `rxjs-prefer-angular-async-pipe` | Disallows the calling of `subscribe` within an Angular component. | None |
| `rxjs-prefer-observer` | Enforces the passing of observers to `subscribe` and `tap`. See [this RxJS issue](https://github.com/ReactiveX/rxjs/issues/4159). | [See below](#rxjs-prefer-observer) |
| `rxjs-suffix-subjects` | Disalllows subjects that don't end with the specified `suffix` option. | [See below](#rxjs-suffix-subjects) |
Expand Down Expand Up @@ -395,6 +396,59 @@ The following options are equivalent to the rule's default configuration:
}
```

<a name="rxjs-prefer-angular-takeuntil-before-subscribe"></a>

#### rxjs-prefer-angular-takeuntil-before-subscribe

This rule tries to avoid memory leaks in angular components when calling `.subscribe()` without properly unsubscribing
by enforcing the application of the `takeUntil(this.destroy$)` operator before the `.subscribe()`
as well as before certain operators (`publish`, `publishBehavior`, `publishLast`, `publishReplay`, `shareReplay`)
and ensuring the component implements the `ngOnDestroy`
method invoking `this.destroy$.next()` and `this.destroy$.complete()`.

##### Example
This should trigger an error:
```typescript
@Component({
selector: 'app-my',
template: '<div>{{k$ | async}}</div>'
})
class MyComponent {
~~~~~~~~~~~ component containing subscribe must implement the ngOnDestroy() method


k$ = a.pipe(shareReplay(1));
~~~~~~~~~~~~~~ the shareReplay operator used within a component must be preceded by takeUntil

someMethod() {
const e = a.pipe(switchMap(_ => b)).subscribe();
~~~~~~~~~ subscribe within a component must be preceded by takeUntil
}
}
```

while this should be fine:
```typescript
@Component({
selector: 'app-my',
template: '<div>{{k$ | async}}</div>'
})
class MyComponent implements SomeInterface, OnDestroy {
private destroy$: Subject<void> = new Subject<void>();

k$ = a.pipe(takeUntil(this.destroy$), shareReplay(1));

someMethod() {
const e = a.pipe(switchMap(_ => b), takeUntil(this.destroy$)).subscribe();
}

ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
```

<a name="rxjs-prefer-observer"></a>

#### rxjs-prefer-observer
Expand Down
57 changes: 57 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ The package includes the following rules (none of which are enabled by default):
| `rxjs-no-unsafe-takeuntil` | Disallows the application of operators after `takeUntil`. Operators placed after `takeUntil` can effect [subscription leaks](https://medium.com/@cartant/rxjs-avoiding-takeuntil-leaks-fb5182d047ef). | [See below](#rxjs-no-unsafe-takeuntil) |
| `rxjs-no-unused-add` | Disallows the importation of patched observables or operators that are not used in the module. | None |
| `rxjs-no-wholesale` | Disallows the wholesale importation of `rxjs` or `rxjs/Rx`. | None |
| `rxjs-prefer-angular-takeuntil-before-subscribe` | Enforces the application of the `takeUntil` operator when calling of `subscribe` within an Angular component. | [See below](#rxjs-prefer-angular-takeuntil-before-subscribe) |
| `rxjs-prefer-angular-async-pipe` | Disallows the calling of `subscribe` within an Angular component. | None |
| `rxjs-prefer-observer` | Enforces the passing of observers to `subscribe` and `tap`. See [this RxJS issue](https://github.com/ReactiveX/rxjs/issues/4159). | [See below](#rxjs-prefer-observer) |
| `rxjs-suffix-subjects` | Disalllows subjects that don't end with the specified `suffix` option. | [See below](#rxjs-suffix-subjects) |
Expand Down Expand Up @@ -331,6 +332,62 @@ The following options are equivalent to the rule's default configuration:
}
```

<a name="rxjs-angular-prefer-takeuntil-before-subscribe"></a>


<a name="rxjs-prefer-angular-takeuntil-before-subscribe"></a>

#### rxjs-prefer-angular-takeuntil-before-subscribe

This rule tries to avoid memory leaks in angular components when calling `.subscribe()` without properly unsubscribing
by enforcing the application of the `takeUntil(this.destroy$)` operator before the `.subscribe()`
as well as before certain operators (`publish`, `publishBehavior`, `publishLast`, `publishReplay`, `shareReplay`)
and ensuring the component implements the `ngOnDestroy`
method invoking `this.destroy$.next()` and `this.destroy$.complete()`.

##### Example
This should trigger an error:
```typescript
@Component({
selector: 'app-my',
template: '<div>{{k$ | async}}</div>'
})
class MyComponent {
~~~~~~~~~~~ component containing subscribe must implement the ngOnDestroy() method


k$ = a.pipe(shareReplay(1));
~~~~~~~~~~~~~~ the shareReplay operator used within a component must be preceded by takeUntil

someMethod() {
const e = a.pipe(switchMap(_ => b)).subscribe();
~~~~~~~~~ subscribe within a component must be preceded by takeUntil
}
}
```

while this should be fine:
```typescript
@Component({
selector: 'app-my',
template: '<div>{{k$ | async}}</div>'
})
class MyComponent implements SomeInterface, OnDestroy {
private destroy$: Subject<void> = new Subject<void>();

k$ = a.pipe(takeUntil(this.destroy$), shareReplay(1));

someMethod() {
const e = a.pipe(switchMap(_ => b), takeUntil(this.destroy$)).subscribe();
}

ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
```

<a name="rxjs-prefer-observer"></a>

#### rxjs-prefer-observer
Expand Down
Loading