# luftborn-rxjs-takeuntil-destroy

An ESLint plugin that enforces the use of `takeUntilDestroyed` or other take operators in RxJS subscription pipes to prevent memory leaks in Angular applications.

## Description

This ESLint rule helps prevent memory leaks in Angular applications by ensuring that all Observable subscriptions use either the `takeUntilDestroyed` operator from `@angular/core/rxjs-interop` or one of the RxJS take operators (`takeUntil`, `takeWhile`, `take`, `takeLast`). These operators ensure that subscriptions are properly terminated, preventing memory leaks.

## Installation

```bash
npm install luftborn-rxjs-takeuntil-destroy --save-dev
```

## Requirements

- ESLint 6.0.0 or higher (compatible with ESLint 9+)
- Node.js 14.0.0 or higher
- Angular 16.0.0 or higher (for @angular/core/rxjs-interop)
- RxJS 7.0.0 or higher

## Usage

Add the plugin to your ESLint configuration:

### ESLint Config (eslintrc.js / eslintrc.json)

```json
{
  "plugins": ["luftborn-rxjs-takeuntil-destroy"],
  "rules": {
    "luftborn-rxjs-takeuntil-destroy/require-take-until-destroyed": "error"
  }
}
```

### ESLint Flat Config (eslint.config.js)

```js
const luftbornRxjsTakeuntilDestroy = require('luftborn-rxjs-takeuntil-destroy');

module.exports = [
  {
    plugins: {
      'luftborn-rxjs-takeuntil-destroy': luftbornRxjsTakeuntilDestroy
    },
    rules: {
      'luftborn-rxjs-takeuntil-destroy/require-take-until-destroyed': 'error'
    }
  }
];
```

## Rule Details

The `require-take-until-destroyed` rule enforces that all Observable subscriptions use either the `takeUntilDestroyed` operator or one of the RxJS take operators (`takeUntil`, `takeWhile`, `take`, `takeLast`) to ensure subscriptions are properly terminated, preventing memory leaks.

This rule is auto-fixable. When you run ESLint with the `--fix` option, it will automatically add `takeUntilDestroyed()` to your subscription pipes where needed.

### Examples

#### ❌ Incorrect

```typescript
// Missing takeUntilDestroyed operator
this.observable.subscribe(result => {
  console.log(result);
});

// Using pipe but missing takeUntilDestroyed
this.observable.pipe(
  map(data => data)
).subscribe(result => {
  console.log(result);
});
```

#### ✅ Correct

```typescript
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

// Using takeUntilDestroyed in pipe
this.observable.pipe(
  map(data => data),
  takeUntilDestroyed()
).subscribe(result => {
  console.log(result);
});

// Using renamed import
import { takeUntilDestroyed as tud } from '@angular/core/rxjs-interop';

this.observable.pipe(
  map(data => data),
  tud()
).subscribe(result => {
  console.log(result);
});

// Using a variable with 'destroy' in the name
const destroyRef = inject(DestroyRef);
const untilDestroyed = takeUntilDestroyed(destroyRef);

this.observable.pipe(
  map(data => data),
  untilDestroyed
).subscribe(result => {
  console.log(result);
});

// Using takeUntil from RxJS
import { takeUntil } from 'rxjs';

destroy$ = new Subject<void>();

this.observable.pipe(
  map(data => data),
  takeUntil(this.destroy$)
).subscribe(result => {
  console.log(result);
});

// Using takeWhile from RxJS
import { takeWhile } from 'rxjs';

alive = true;

this.observable.pipe(
  map(data => data),
  takeWhile(() => this.alive)
).subscribe(result => {
  console.log(result);
});

// Using take from RxJS
import { take } from 'rxjs';

this.observable.pipe(
  map(data => data),
  take(1)
).subscribe(result => {
  console.log(result);
});

// Using takeLast from RxJS
import { takeLast } from 'rxjs';

this.observable.pipe(
  map(data => data),
  takeLast(1)
).subscribe(result => {
  console.log(result);
});
```

## Rule Options

This rule has no options.

### Auto-fix Behavior

When auto-fix is applied:

1. For subscriptions without a pipe call, it adds a pipe with `takeUntilDestroyed()` before the subscribe call.
2. For subscriptions with a pipe call but missing a take operator, it adds `takeUntilDestroyed()` as the last operator in the pipe.

Note: The auto-fix assumes that `takeUntilDestroyed` is imported from `@angular/core/rxjs-interop`. If it's not imported, you'll need to add the import manually.

## Implementation Details

The rule checks for:

1. Observable subscriptions without a pipe call
2. Observable subscriptions with a pipe call but missing any of the supported take operators
3. Proper import and usage of the `takeUntilDestroyed` operator from `@angular/core/rxjs-interop`
4. Proper import and usage of RxJS take operators (`takeUntil`, `takeWhile`, `take`, `takeLast`) from 'rxjs'

The rule recognizes:
- Renamed imports for all supported operators
- Variables that might contain any of the supported operators
- Various patterns for subscription cleanup

## License

MIT © Luftborn

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

1. Fork the repository https://github.com/Arigatouz/luftborn-rxjs-takeuntil-destroy
2. Create your feature branch (`git switch -c feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
