eslint-plugin-rxjs-x 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -108,52 +108,54 @@ The package includes the following rules.
108
108
  💭 Requires [type information](https://typescript-eslint.io/linting/typed-linting).\
109
109
  ❌ Deprecated.
110
110
 
111
- | Name                       | Description | 💼 | 🔧 | 💡 | 💭 | ❌ |
112
- | :--------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------- | :--- | :- | :- | :- | :- |
113
- | [ban-observables](docs/rules/ban-observables.md) | Disallow banned observable creators. | | | | | |
114
- | [ban-operators](docs/rules/ban-operators.md) | Disallow banned operators. | | | | 💭 | |
115
- | [finnish](docs/rules/finnish.md) | Enforce Finnish notation. | | | | 💭 | |
116
- | [just](docs/rules/just.md) | Require the use of `just` instead of `of`. | | 🔧 | | | |
117
- | [macro](docs/rules/macro.md) | Require the use of the RxJS Tools Babel macro. | | 🔧 | | | ❌ |
118
- | [no-async-subscribe](docs/rules/no-async-subscribe.md) | Disallow passing `async` functions to `subscribe`. | ✅ 🔒 | | | 💭 | |
119
- | [no-compat](docs/rules/no-compat.md) | Disallow the `rxjs-compat` package. | | | | | ❌ |
120
- | [no-connectable](docs/rules/no-connectable.md) | Disallow operators that return connectable observables. | | | | 💭 | |
121
- | [no-create](docs/rules/no-create.md) | Disallow the static `Observable.create` function. | ✅ 🔒 | | | 💭 | |
122
- | [no-cyclic-action](docs/rules/no-cyclic-action.md) | Disallow cyclic actions in effects and epics. | | | | 💭 | |
123
- | [no-explicit-generics](docs/rules/no-explicit-generics.md) | Disallow unnecessary explicit generic type arguments. | | | | | |
124
- | [no-exposed-subjects](docs/rules/no-exposed-subjects.md) | Disallow public and protected subjects. | 🔒 | | | 💭 | |
125
- | [no-finnish](docs/rules/no-finnish.md) | Disallow Finnish notation. | | | | 💭 | |
126
- | [no-floating-observables](docs/rules/no-floating-observables.md) | Require Observables to be handled appropriately. | 🔒 | | | 💭 | |
127
- | [no-ignored-default-value](docs/rules/no-ignored-default-value.md) | Disallow using `firstValueFrom`, `lastValueFrom`, `first`, and `last` without specifying a default value. | 🔒 | | | 💭 | |
128
- | [no-ignored-error](docs/rules/no-ignored-error.md) | Disallow calling `subscribe` without specifying an error handler. | 🔒 | | | 💭 | |
129
- | [no-ignored-notifier](docs/rules/no-ignored-notifier.md) | Disallow observables not composed from the `repeatWhen` or `retryWhen` notifier. | ✅ 🔒 | | | 💭 | |
130
- | [no-ignored-replay-buffer](docs/rules/no-ignored-replay-buffer.md) | Disallow using `ReplaySubject`, `publishReplay` or `shareReplay` without specifying the buffer size. | ✅ 🔒 | | | | |
131
- | [no-ignored-subscribe](docs/rules/no-ignored-subscribe.md) | Disallow calling `subscribe` without specifying arguments. | | | | 💭 | |
132
- | [no-ignored-subscription](docs/rules/no-ignored-subscription.md) | Disallow ignoring the subscription returned by `subscribe`. | | | | 💭 | |
133
- | [no-ignored-takewhile-value](docs/rules/no-ignored-takewhile-value.md) | Disallow ignoring the value within `takeWhile`. | ✅ 🔒 | | | | |
134
- | [no-implicit-any-catch](docs/rules/no-implicit-any-catch.md) | Disallow implicit `any` error parameters in `catchError` operators. | ✅ 🔒 | 🔧 | 💡 | 💭 | |
135
- | [no-index](docs/rules/no-index.md) | Disallow importing index modules. | ✅ 🔒 | | | | |
136
- | [no-internal](docs/rules/no-internal.md) | Disallow importing internal modules. | ✅ 🔒 | 🔧 | 💡 | | |
137
- | [no-misused-observables](docs/rules/no-misused-observables.md) | Disallow Observables in places not designed to handle them. | 🔒 | | | 💭 | |
138
- | [no-nested-subscribe](docs/rules/no-nested-subscribe.md) | Disallow calling `subscribe` within a `subscribe` callback. | ✅ 🔒 | | | 💭 | |
139
- | [no-redundant-notify](docs/rules/no-redundant-notify.md) | Disallow sending redundant notifications from completed or errored observables. | ✅ 🔒 | | | 💭 | |
140
- | [no-sharereplay](docs/rules/no-sharereplay.md) | Disallow unsafe `shareReplay` usage. | ✅ 🔒 | | | | |
141
- | [no-subclass](docs/rules/no-subclass.md) | Disallow subclassing RxJS classes. | 🔒 | | | 💭 | |
142
- | [no-subject-unsubscribe](docs/rules/no-subject-unsubscribe.md) | Disallow calling the `unsubscribe` method of subjects. | 🔒 | | | 💭 | |
143
- | [no-subject-value](docs/rules/no-subject-value.md) | Disallow accessing the `value` property of a `BehaviorSubject` instance. | | | | 💭 | |
144
- | [no-subscribe-handlers](docs/rules/no-subscribe-handlers.md) | Disallow passing handlers to `subscribe`. | | | | 💭 | |
145
- | [no-subscribe-in-pipe](docs/rules/no-subscribe-in-pipe.md) | Disallow calling of `subscribe` within any RxJS operator inside a `pipe`. | ✅ 🔒 | | | 💭 | |
146
- | [no-tap](docs/rules/no-tap.md) | Disallow the `tap` operator. | | | | | ❌ |
147
- | [no-topromise](docs/rules/no-topromise.md) | Disallow use of the `toPromise` method. | ✅ 🔒 | | 💡 | 💭 | |
148
- | [no-unbound-methods](docs/rules/no-unbound-methods.md) | Disallow passing unbound methods. | ✅ 🔒 | | | 💭 | |
149
- | [no-unsafe-catch](docs/rules/no-unsafe-catch.md) | Disallow unsafe `catchError` usage in effects and epics. | | | | 💭 | |
150
- | [no-unsafe-first](docs/rules/no-unsafe-first.md) | Disallow unsafe `first`/`take` usage in effects and epics. | | | | 💭 | |
151
- | [no-unsafe-subject-next](docs/rules/no-unsafe-subject-next.md) | Disallow unsafe optional `next` calls. | 🔒 | | | 💭 | |
152
- | [no-unsafe-switchmap](docs/rules/no-unsafe-switchmap.md) | Disallow unsafe `switchMap` usage in effects and epics. | | | | 💭 | |
153
- | [no-unsafe-takeuntil](docs/rules/no-unsafe-takeuntil.md) | Disallow applying operators after `takeUntil`. | ✅ 🔒 | | | 💭 | |
154
- | [prefer-observer](docs/rules/prefer-observer.md) | Disallow passing separate handlers to `subscribe` and `tap`. | 🔒 | 🔧 | 💡 | 💭 | |
155
- | [prefer-root-operators](docs/rules/prefer-root-operators.md) | Disallow importing operators from `rxjs/operators`. | ✅ 🔒 | 🔧 | 💡 | | |
156
- | [suffix-subjects](docs/rules/suffix-subjects.md) | Enforce the use of a suffix in subject identifiers. | | | | 💭 | |
157
- | [throw-error](docs/rules/throw-error.md) | Enforce passing only `Error` values to `throwError` or `Subject.error`. | ✅ 🔒 | | | 💭 | |
111
+ | Name                            | Description | 💼 | 🔧 | 💡 | 💭 | ❌ |
112
+ | :------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------- | :--- | :- | :- | :- | :- |
113
+ | [ban-observables](docs/rules/ban-observables.md) | Disallow banned observable creators. | | | | | |
114
+ | [ban-operators](docs/rules/ban-operators.md) | Disallow banned operators. | | | | 💭 | |
115
+ | [finnish](docs/rules/finnish.md) | Enforce Finnish notation. | | | | 💭 | |
116
+ | [just](docs/rules/just.md) | Require the use of `just` instead of `of`. | | 🔧 | | | |
117
+ | [macro](docs/rules/macro.md) | Require the use of the RxJS Tools Babel macro. | | 🔧 | | | ❌ |
118
+ | [no-async-subscribe](docs/rules/no-async-subscribe.md) | Disallow passing `async` functions to `subscribe`. | ✅ 🔒 | | | 💭 | |
119
+ | [no-compat](docs/rules/no-compat.md) | Disallow the `rxjs-compat` package. | | | | | ❌ |
120
+ | [no-connectable](docs/rules/no-connectable.md) | Disallow operators that return connectable observables. | | | | 💭 | |
121
+ | [no-create](docs/rules/no-create.md) | Disallow the static `Observable.create` function. | ✅ 🔒 | | | 💭 | |
122
+ | [no-cyclic-action](docs/rules/no-cyclic-action.md) | Disallow cyclic actions in effects and epics. | | | | 💭 | |
123
+ | [no-explicit-generics](docs/rules/no-explicit-generics.md) | Disallow unnecessary explicit generic type arguments. | | | | | |
124
+ | [no-exposed-subjects](docs/rules/no-exposed-subjects.md) | Disallow public and protected subjects. | 🔒 | | | 💭 | |
125
+ | [no-finnish](docs/rules/no-finnish.md) | Disallow Finnish notation. | | | | 💭 | |
126
+ | [no-floating-observables](docs/rules/no-floating-observables.md) | Require Observables to be handled appropriately. | 🔒 | | | 💭 | |
127
+ | [no-ignored-default-value](docs/rules/no-ignored-default-value.md) | Disallow using `firstValueFrom`, `lastValueFrom`, `first`, and `last` without specifying a default value. | 🔒 | | | 💭 | |
128
+ | [no-ignored-error](docs/rules/no-ignored-error.md) | Disallow calling `subscribe` without specifying an error handler. | 🔒 | | | 💭 | |
129
+ | [no-ignored-notifier](docs/rules/no-ignored-notifier.md) | Disallow observables not composed from the `repeatWhen` or `retryWhen` notifier. | ✅ 🔒 | | | 💭 | |
130
+ | [no-ignored-replay-buffer](docs/rules/no-ignored-replay-buffer.md) | Disallow using `ReplaySubject`, `publishReplay` or `shareReplay` without specifying the buffer size. | ✅ 🔒 | | | | |
131
+ | [no-ignored-subscribe](docs/rules/no-ignored-subscribe.md) | Disallow calling `subscribe` without specifying arguments. | | | | 💭 | |
132
+ | [no-ignored-subscription](docs/rules/no-ignored-subscription.md) | Disallow ignoring the subscription returned by `subscribe`. | | | | 💭 | |
133
+ | [no-ignored-takewhile-value](docs/rules/no-ignored-takewhile-value.md) | Disallow ignoring the value within `takeWhile`. | ✅ 🔒 | | | | |
134
+ | [no-implicit-any-catch](docs/rules/no-implicit-any-catch.md) | Disallow implicit `any` error parameters in `catchError` operators. | ✅ 🔒 | 🔧 | 💡 | 💭 | |
135
+ | [no-index](docs/rules/no-index.md) | Disallow importing index modules. | ✅ 🔒 | | | | |
136
+ | [no-internal](docs/rules/no-internal.md) | Disallow importing internal modules. | ✅ 🔒 | 🔧 | 💡 | | |
137
+ | [no-misused-observables](docs/rules/no-misused-observables.md) | Disallow Observables in places not designed to handle them. | 🔒 | | | 💭 | |
138
+ | [no-nested-subscribe](docs/rules/no-nested-subscribe.md) | Disallow calling `subscribe` within a `subscribe` callback. | ✅ 🔒 | | | 💭 | |
139
+ | [no-redundant-notify](docs/rules/no-redundant-notify.md) | Disallow sending redundant notifications from completed or errored observables. | ✅ 🔒 | | | 💭 | |
140
+ | [no-sharereplay](docs/rules/no-sharereplay.md) | Disallow unsafe `shareReplay` usage. | ✅ 🔒 | | | | |
141
+ | [no-sharereplay-before-takeuntil](docs/rules/no-sharereplay-before-takeuntil.md) | Disallow using `shareReplay({ refCount: false })` before `takeUntil`. | 🔒 | | | | |
142
+ | [no-subclass](docs/rules/no-subclass.md) | Disallow subclassing RxJS classes. | 🔒 | | | 💭 | |
143
+ | [no-subject-unsubscribe](docs/rules/no-subject-unsubscribe.md) | Disallow calling the `unsubscribe` method of subjects. | 🔒 | | | 💭 | |
144
+ | [no-subject-value](docs/rules/no-subject-value.md) | Disallow accessing the `value` property of a `BehaviorSubject` instance. | | | | 💭 | |
145
+ | [no-subscribe-handlers](docs/rules/no-subscribe-handlers.md) | Disallow passing handlers to `subscribe`. | | | | 💭 | |
146
+ | [no-subscribe-in-pipe](docs/rules/no-subscribe-in-pipe.md) | Disallow calling of `subscribe` within any RxJS operator inside a `pipe`. | ✅ 🔒 | | | 💭 | |
147
+ | [no-tap](docs/rules/no-tap.md) | Disallow the `tap` operator. | | | | | |
148
+ | [no-topromise](docs/rules/no-topromise.md) | Disallow use of the `toPromise` method. | ✅ 🔒 | | 💡 | 💭 | |
149
+ | [no-unbound-methods](docs/rules/no-unbound-methods.md) | Disallow passing unbound methods. | 🔒 | | | 💭 | |
150
+ | [no-unnecessary-collection](docs/rules/no-unnecessary-collection.md) | Disallow unnecessary usage of collection arguments with single values. | 🔒 | | | | |
151
+ | [no-unsafe-catch](docs/rules/no-unsafe-catch.md) | Disallow unsafe `catchError` usage in effects and epics. | | | | 💭 | |
152
+ | [no-unsafe-first](docs/rules/no-unsafe-first.md) | Disallow unsafe `first`/`take` usage in effects and epics. | | | | 💭 | |
153
+ | [no-unsafe-subject-next](docs/rules/no-unsafe-subject-next.md) | Disallow unsafe optional `next` calls. | ✅ 🔒 | | | 💭 | |
154
+ | [no-unsafe-switchmap](docs/rules/no-unsafe-switchmap.md) | Disallow unsafe `switchMap` usage in effects and epics. | | | | 💭 | |
155
+ | [no-unsafe-takeuntil](docs/rules/no-unsafe-takeuntil.md) | Disallow applying operators after `takeUntil`. | ✅ 🔒 | | | 💭 | |
156
+ | [prefer-observer](docs/rules/prefer-observer.md) | Disallow passing separate handlers to `subscribe` and `tap`. | ✅ 🔒 | 🔧 | 💡 | 💭 | |
157
+ | [prefer-root-operators](docs/rules/prefer-root-operators.md) | Disallow importing operators from `rxjs/operators`. | ✅ 🔒 | 🔧 | 💡 | | |
158
+ | [suffix-subjects](docs/rules/suffix-subjects.md) | Enforce the use of a suffix in subject identifiers. | | | | 💭 | |
159
+ | [throw-error](docs/rules/throw-error.md) | Enforce passing only `Error` values to `throwError` or `Subject.error`. | ✅ 🔒 | | | 💭 | |
158
160
 
159
161
  <!-- end auto-generated rules list -->
package/dist/index.d.mts CHANGED
@@ -159,6 +159,10 @@ declare const allRules: {
159
159
  description: "Disallow unsafe `shareReplay` usage.";
160
160
  recommended: "recommended";
161
161
  }, TSESLint.RuleListener>;
162
+ 'no-sharereplay-before-takeuntil': TSESLint.RuleModule<"forbidden", [], {
163
+ description: "Disallow using `shareReplay({ refCount: false })` before `takeUntil`.";
164
+ recommended: "strict";
165
+ }, TSESLint.RuleListener>;
162
166
  'no-subclass': TSESLint.RuleModule<"forbidden", [], {
163
167
  description: "Disallow subclassing RxJS classes.";
164
168
  recommended: "strict";
@@ -197,6 +201,11 @@ declare const allRules: {
197
201
  recommended: "recommended";
198
202
  requiresTypeChecking: true;
199
203
  }, TSESLint.RuleListener>;
204
+ 'no-unnecessary-collection': TSESLint.RuleModule<"forbidden", [], {
205
+ description: "Disallow unnecessary usage of collection arguments with single values.";
206
+ recommended: "strict";
207
+ requiresTypeChecking: false;
208
+ }, TSESLint.RuleListener>;
200
209
  'no-unsafe-catch': TSESLint.RuleModule<"forbidden", readonly {
201
210
  observable?: string;
202
211
  }[], {
@@ -319,12 +328,14 @@ declare const rxjsX: {
319
328
  'rxjs-x/no-misused-observables': "error";
320
329
  'rxjs-x/no-nested-subscribe': "error";
321
330
  'rxjs-x/no-redundant-notify': "error";
331
+ 'rxjs-x/no-sharereplay-before-takeuntil': "error";
322
332
  'rxjs-x/no-sharereplay': "error";
323
333
  'rxjs-x/no-subclass': "error";
324
334
  'rxjs-x/no-subject-unsubscribe': "error";
325
335
  'rxjs-x/no-subscribe-in-pipe': "error";
326
336
  'rxjs-x/no-topromise': "error";
327
337
  'rxjs-x/no-unbound-methods': "error";
338
+ 'rxjs-x/no-unnecessary-collection': "error";
328
339
  'rxjs-x/no-unsafe-subject-next': "error";
329
340
  'rxjs-x/no-unsafe-takeuntil': "error";
330
341
  'rxjs-x/prefer-observer': "error";
package/dist/index.d.ts CHANGED
@@ -159,6 +159,10 @@ declare const allRules: {
159
159
  description: "Disallow unsafe `shareReplay` usage.";
160
160
  recommended: "recommended";
161
161
  }, TSESLint.RuleListener>;
162
+ 'no-sharereplay-before-takeuntil': TSESLint.RuleModule<"forbidden", [], {
163
+ description: "Disallow using `shareReplay({ refCount: false })` before `takeUntil`.";
164
+ recommended: "strict";
165
+ }, TSESLint.RuleListener>;
162
166
  'no-subclass': TSESLint.RuleModule<"forbidden", [], {
163
167
  description: "Disallow subclassing RxJS classes.";
164
168
  recommended: "strict";
@@ -197,6 +201,11 @@ declare const allRules: {
197
201
  recommended: "recommended";
198
202
  requiresTypeChecking: true;
199
203
  }, TSESLint.RuleListener>;
204
+ 'no-unnecessary-collection': TSESLint.RuleModule<"forbidden", [], {
205
+ description: "Disallow unnecessary usage of collection arguments with single values.";
206
+ recommended: "strict";
207
+ requiresTypeChecking: false;
208
+ }, TSESLint.RuleListener>;
200
209
  'no-unsafe-catch': TSESLint.RuleModule<"forbidden", readonly {
201
210
  observable?: string;
202
211
  }[], {
@@ -319,12 +328,14 @@ declare const rxjsX: {
319
328
  'rxjs-x/no-misused-observables': "error";
320
329
  'rxjs-x/no-nested-subscribe': "error";
321
330
  'rxjs-x/no-redundant-notify': "error";
331
+ 'rxjs-x/no-sharereplay-before-takeuntil': "error";
322
332
  'rxjs-x/no-sharereplay': "error";
323
333
  'rxjs-x/no-subclass': "error";
324
334
  'rxjs-x/no-subject-unsubscribe': "error";
325
335
  'rxjs-x/no-subscribe-in-pipe': "error";
326
336
  'rxjs-x/no-topromise': "error";
327
337
  'rxjs-x/no-unbound-methods': "error";
338
+ 'rxjs-x/no-unnecessary-collection': "error";
328
339
  'rxjs-x/no-unsafe-subject-next': "error";
329
340
  'rxjs-x/no-unsafe-takeuntil': "error";
330
341
  'rxjs-x/prefer-observer': "error";
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }// package.json
2
2
  var name = "eslint-plugin-rxjs-x";
3
- var version = "0.8.0";
3
+ var version = "0.8.1";
4
4
 
5
5
  // src/configs/recommended.ts
6
6
  var createRecommendedConfig = (plugin2) => ({
@@ -58,12 +58,14 @@ var createStrictConfig = (plugin2) => ({
58
58
  "rxjs-x/no-misused-observables": "error",
59
59
  "rxjs-x/no-nested-subscribe": "error",
60
60
  "rxjs-x/no-redundant-notify": "error",
61
+ "rxjs-x/no-sharereplay-before-takeuntil": "error",
61
62
  "rxjs-x/no-sharereplay": "error",
62
63
  "rxjs-x/no-subclass": "error",
63
64
  "rxjs-x/no-subject-unsubscribe": "error",
64
65
  "rxjs-x/no-subscribe-in-pipe": "error",
65
66
  "rxjs-x/no-topromise": "error",
66
67
  "rxjs-x/no-unbound-methods": "error",
68
+ "rxjs-x/no-unnecessary-collection": "error",
67
69
  "rxjs-x/no-unsafe-subject-next": "error",
68
70
  "rxjs-x/no-unsafe-takeuntil": "error",
69
71
  "rxjs-x/prefer-observer": "error",
@@ -403,6 +405,14 @@ var SOURCES_OBJECT_ACCEPTING_STATIC_OBSERVABLE_CREATORS = [
403
405
  "combineLatest",
404
406
  "forkJoin"
405
407
  ];
408
+ var MULTIPLE_OBSERVABLE_ACCEPTING_STATIC_OBSERVABLE_CREATORS = [
409
+ "combineLatest",
410
+ "forkJoin",
411
+ "merge",
412
+ "zip",
413
+ "concat",
414
+ "race"
415
+ ];
406
416
  var DEFAULT_UNBOUND_ALLOWED_TYPES = [
407
417
  "Signal"
408
418
  ];
@@ -2825,6 +2835,65 @@ var noSharereplayRule = ruleCreator({
2825
2835
  }
2826
2836
  });
2827
2837
 
2838
+ // src/rules/no-sharereplay-before-takeuntil.ts
2839
+ var noSharereplayBeforeTakeuntilRule = ruleCreator({
2840
+ defaultOptions: [],
2841
+ meta: {
2842
+ docs: {
2843
+ description: "Disallow using `shareReplay({ refCount: false })` before `takeUntil`.",
2844
+ recommended: "strict"
2845
+ },
2846
+ messages: {
2847
+ forbidden: "shareReplay before takeUntil is forbidden unless 'refCount: true' is specified."
2848
+ },
2849
+ schema: [],
2850
+ type: "problem"
2851
+ },
2852
+ name: "no-sharereplay-before-takeuntil",
2853
+ create: (context) => {
2854
+ function checkCallExpression(node) {
2855
+ const pipeCallExpression = node.parent;
2856
+ if (!pipeCallExpression.arguments || !isMemberExpression(pipeCallExpression.callee) || !isIdentifier(pipeCallExpression.callee.property) || pipeCallExpression.callee.property.name !== "pipe") {
2857
+ return;
2858
+ }
2859
+ const { isOrderValid, operatorNode: takeUntilNode } = findIsLastOperatorOrderValid(
2860
+ pipeCallExpression,
2861
+ /^takeUntil$/,
2862
+ DEFAULT_VALID_POST_COMPLETION_OPERATORS
2863
+ );
2864
+ if (!isOrderValid || !takeUntilNode) {
2865
+ return;
2866
+ }
2867
+ if (takeUntilNode.range[0] < node.range[0]) {
2868
+ return;
2869
+ }
2870
+ const shareReplayConfig = node.arguments[0];
2871
+ if (!shareReplayConfig || !isObjectExpression(shareReplayConfig)) {
2872
+ context.report({
2873
+ messageId: "forbidden",
2874
+ node: node.callee
2875
+ });
2876
+ return;
2877
+ }
2878
+ const refCountElement = shareReplayConfig.properties.filter(isProperty).find((prop) => isIdentifier(prop.key) && prop.key.name === "refCount");
2879
+ if (!refCountElement || isLiteral(refCountElement.value) && refCountElement.value.value === false) {
2880
+ context.report({
2881
+ messageId: "forbidden",
2882
+ node: node.callee
2883
+ });
2884
+ }
2885
+ }
2886
+ return {
2887
+ 'CallExpression[callee.name="shareReplay"]': (node) => {
2888
+ checkCallExpression(node);
2889
+ },
2890
+ 'CallExpression[callee.property.name="shareReplay"]': (node) => {
2891
+ checkCallExpression(node);
2892
+ }
2893
+ };
2894
+ }
2895
+ });
2896
+
2828
2897
  // src/rules/no-subclass.ts
2829
2898
  var noSubclassRule = ruleCreator({
2830
2899
  defaultOptions: [],
@@ -3235,6 +3304,94 @@ var noUnboundMethodsRule = ruleCreator({
3235
3304
  }
3236
3305
  });
3237
3306
 
3307
+ // src/rules/no-unnecessary-collection.ts
3308
+ var noUnnecessaryCollectionRule = ruleCreator({
3309
+ defaultOptions: [],
3310
+ meta: {
3311
+ docs: {
3312
+ description: "Disallow unnecessary usage of collection arguments with single values.",
3313
+ recommended: "strict",
3314
+ requiresTypeChecking: false
3315
+ },
3316
+ messages: {
3317
+ forbidden: "Unnecessary {{operator}} with {{inputType}}. Use the observable directly instead."
3318
+ },
3319
+ schema: [],
3320
+ type: "suggestion"
3321
+ },
3322
+ name: "no-unnecessary-collection",
3323
+ create: (context) => {
3324
+ const { couldBeType: couldBeType2, couldBeObservable } = getTypeServices(context);
3325
+ function couldBeFromRxjs(node, operatorName) {
3326
+ return couldBeType2(node, operatorName, { name: /[/\\]rxjs[/\\]/ });
3327
+ }
3328
+ function checkCallExpression(node, operatorName) {
3329
+ const args = node.arguments;
3330
+ if (args.length === 0) {
3331
+ return;
3332
+ }
3333
+ const firstArg = args[0];
3334
+ if (isArrayExpression(firstArg)) {
3335
+ const nonNullElements = firstArg.elements.filter((element) => element !== null);
3336
+ if (nonNullElements.length === 1 && couldBeObservable(nonNullElements[0])) {
3337
+ context.report({
3338
+ messageId: "forbidden",
3339
+ node: node.callee,
3340
+ data: {
3341
+ operator: operatorName,
3342
+ inputType: "single-valued array"
3343
+ }
3344
+ });
3345
+ }
3346
+ return;
3347
+ }
3348
+ if (isObjectExpression(firstArg) && SOURCES_OBJECT_ACCEPTING_STATIC_OBSERVABLE_CREATORS.includes(operatorName)) {
3349
+ if (firstArg.properties.length === 1 && isProperty(firstArg.properties[0])) {
3350
+ const property = firstArg.properties[0];
3351
+ if (property.value && couldBeObservable(property.value)) {
3352
+ context.report({
3353
+ messageId: "forbidden",
3354
+ node: node.callee,
3355
+ data: {
3356
+ operator: operatorName,
3357
+ inputType: "single-property object"
3358
+ }
3359
+ });
3360
+ }
3361
+ }
3362
+ return;
3363
+ }
3364
+ if (args.length === 1 && couldBeObservable(firstArg)) {
3365
+ context.report({
3366
+ messageId: "forbidden",
3367
+ node: node.callee,
3368
+ data: {
3369
+ operator: operatorName,
3370
+ inputType: "single argument"
3371
+ }
3372
+ });
3373
+ }
3374
+ }
3375
+ const callExpressionVisitors = {};
3376
+ for (const operator of MULTIPLE_OBSERVABLE_ACCEPTING_STATIC_OBSERVABLE_CREATORS) {
3377
+ callExpressionVisitors[`CallExpression[callee.name="${operator}"]`] = (node) => {
3378
+ if (couldBeFromRxjs(node.callee, operator)) {
3379
+ checkCallExpression(node, operator);
3380
+ }
3381
+ };
3382
+ }
3383
+ for (const operator of MULTIPLE_OBSERVABLE_ACCEPTING_STATIC_OBSERVABLE_CREATORS) {
3384
+ callExpressionVisitors[`CallExpression[callee.type="MemberExpression"][callee.property.name="${operator}"]`] = (node) => {
3385
+ const memberExpr = node.callee;
3386
+ if (couldBeFromRxjs(memberExpr.property, operator)) {
3387
+ checkCallExpression(node, operator);
3388
+ }
3389
+ };
3390
+ }
3391
+ return callExpressionVisitors;
3392
+ }
3393
+ });
3394
+
3238
3395
  // src/rules/no-unsafe-catch.ts
3239
3396
 
3240
3397
  var defaultOptions12 = [];
@@ -4158,6 +4315,7 @@ var allRules = {
4158
4315
  "no-nested-subscribe": noNestedSubscribeRule,
4159
4316
  "no-redundant-notify": noRedundantNotifyRule,
4160
4317
  "no-sharereplay": noSharereplayRule,
4318
+ "no-sharereplay-before-takeuntil": noSharereplayBeforeTakeuntilRule,
4161
4319
  "no-subclass": noSubclassRule,
4162
4320
  "no-subject-unsubscribe": noSubjectUnsubscribeRule,
4163
4321
  "no-subject-value": noSubjectValueRule,
@@ -4166,6 +4324,7 @@ var allRules = {
4166
4324
  "no-tap": noTapRule,
4167
4325
  "no-topromise": noTopromiseRule,
4168
4326
  "no-unbound-methods": noUnboundMethodsRule,
4327
+ "no-unnecessary-collection": noUnnecessaryCollectionRule,
4169
4328
  "no-unsafe-catch": noUnsafeCatchRule,
4170
4329
  "no-unsafe-first": noUnsafeFirstRule,
4171
4330
  "no-unsafe-subject-next": noUnsafeSubjectNext,
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  // package.json
2
2
  var name = "eslint-plugin-rxjs-x";
3
- var version = "0.8.0";
3
+ var version = "0.8.1";
4
4
 
5
5
  // src/configs/recommended.ts
6
6
  var createRecommendedConfig = (plugin2) => ({
@@ -58,12 +58,14 @@ var createStrictConfig = (plugin2) => ({
58
58
  "rxjs-x/no-misused-observables": "error",
59
59
  "rxjs-x/no-nested-subscribe": "error",
60
60
  "rxjs-x/no-redundant-notify": "error",
61
+ "rxjs-x/no-sharereplay-before-takeuntil": "error",
61
62
  "rxjs-x/no-sharereplay": "error",
62
63
  "rxjs-x/no-subclass": "error",
63
64
  "rxjs-x/no-subject-unsubscribe": "error",
64
65
  "rxjs-x/no-subscribe-in-pipe": "error",
65
66
  "rxjs-x/no-topromise": "error",
66
67
  "rxjs-x/no-unbound-methods": "error",
68
+ "rxjs-x/no-unnecessary-collection": "error",
67
69
  "rxjs-x/no-unsafe-subject-next": "error",
68
70
  "rxjs-x/no-unsafe-takeuntil": "error",
69
71
  "rxjs-x/prefer-observer": "error",
@@ -403,6 +405,14 @@ var SOURCES_OBJECT_ACCEPTING_STATIC_OBSERVABLE_CREATORS = [
403
405
  "combineLatest",
404
406
  "forkJoin"
405
407
  ];
408
+ var MULTIPLE_OBSERVABLE_ACCEPTING_STATIC_OBSERVABLE_CREATORS = [
409
+ "combineLatest",
410
+ "forkJoin",
411
+ "merge",
412
+ "zip",
413
+ "concat",
414
+ "race"
415
+ ];
406
416
  var DEFAULT_UNBOUND_ALLOWED_TYPES = [
407
417
  "Signal"
408
418
  ];
@@ -2825,6 +2835,65 @@ var noSharereplayRule = ruleCreator({
2825
2835
  }
2826
2836
  });
2827
2837
 
2838
+ // src/rules/no-sharereplay-before-takeuntil.ts
2839
+ var noSharereplayBeforeTakeuntilRule = ruleCreator({
2840
+ defaultOptions: [],
2841
+ meta: {
2842
+ docs: {
2843
+ description: "Disallow using `shareReplay({ refCount: false })` before `takeUntil`.",
2844
+ recommended: "strict"
2845
+ },
2846
+ messages: {
2847
+ forbidden: "shareReplay before takeUntil is forbidden unless 'refCount: true' is specified."
2848
+ },
2849
+ schema: [],
2850
+ type: "problem"
2851
+ },
2852
+ name: "no-sharereplay-before-takeuntil",
2853
+ create: (context) => {
2854
+ function checkCallExpression(node) {
2855
+ const pipeCallExpression = node.parent;
2856
+ if (!pipeCallExpression.arguments || !isMemberExpression(pipeCallExpression.callee) || !isIdentifier(pipeCallExpression.callee.property) || pipeCallExpression.callee.property.name !== "pipe") {
2857
+ return;
2858
+ }
2859
+ const { isOrderValid, operatorNode: takeUntilNode } = findIsLastOperatorOrderValid(
2860
+ pipeCallExpression,
2861
+ /^takeUntil$/,
2862
+ DEFAULT_VALID_POST_COMPLETION_OPERATORS
2863
+ );
2864
+ if (!isOrderValid || !takeUntilNode) {
2865
+ return;
2866
+ }
2867
+ if (takeUntilNode.range[0] < node.range[0]) {
2868
+ return;
2869
+ }
2870
+ const shareReplayConfig = node.arguments[0];
2871
+ if (!shareReplayConfig || !isObjectExpression(shareReplayConfig)) {
2872
+ context.report({
2873
+ messageId: "forbidden",
2874
+ node: node.callee
2875
+ });
2876
+ return;
2877
+ }
2878
+ const refCountElement = shareReplayConfig.properties.filter(isProperty).find((prop) => isIdentifier(prop.key) && prop.key.name === "refCount");
2879
+ if (!refCountElement || isLiteral(refCountElement.value) && refCountElement.value.value === false) {
2880
+ context.report({
2881
+ messageId: "forbidden",
2882
+ node: node.callee
2883
+ });
2884
+ }
2885
+ }
2886
+ return {
2887
+ 'CallExpression[callee.name="shareReplay"]': (node) => {
2888
+ checkCallExpression(node);
2889
+ },
2890
+ 'CallExpression[callee.property.name="shareReplay"]': (node) => {
2891
+ checkCallExpression(node);
2892
+ }
2893
+ };
2894
+ }
2895
+ });
2896
+
2828
2897
  // src/rules/no-subclass.ts
2829
2898
  var noSubclassRule = ruleCreator({
2830
2899
  defaultOptions: [],
@@ -3235,6 +3304,94 @@ var noUnboundMethodsRule = ruleCreator({
3235
3304
  }
3236
3305
  });
3237
3306
 
3307
+ // src/rules/no-unnecessary-collection.ts
3308
+ var noUnnecessaryCollectionRule = ruleCreator({
3309
+ defaultOptions: [],
3310
+ meta: {
3311
+ docs: {
3312
+ description: "Disallow unnecessary usage of collection arguments with single values.",
3313
+ recommended: "strict",
3314
+ requiresTypeChecking: false
3315
+ },
3316
+ messages: {
3317
+ forbidden: "Unnecessary {{operator}} with {{inputType}}. Use the observable directly instead."
3318
+ },
3319
+ schema: [],
3320
+ type: "suggestion"
3321
+ },
3322
+ name: "no-unnecessary-collection",
3323
+ create: (context) => {
3324
+ const { couldBeType: couldBeType2, couldBeObservable } = getTypeServices(context);
3325
+ function couldBeFromRxjs(node, operatorName) {
3326
+ return couldBeType2(node, operatorName, { name: /[/\\]rxjs[/\\]/ });
3327
+ }
3328
+ function checkCallExpression(node, operatorName) {
3329
+ const args = node.arguments;
3330
+ if (args.length === 0) {
3331
+ return;
3332
+ }
3333
+ const firstArg = args[0];
3334
+ if (isArrayExpression(firstArg)) {
3335
+ const nonNullElements = firstArg.elements.filter((element) => element !== null);
3336
+ if (nonNullElements.length === 1 && couldBeObservable(nonNullElements[0])) {
3337
+ context.report({
3338
+ messageId: "forbidden",
3339
+ node: node.callee,
3340
+ data: {
3341
+ operator: operatorName,
3342
+ inputType: "single-valued array"
3343
+ }
3344
+ });
3345
+ }
3346
+ return;
3347
+ }
3348
+ if (isObjectExpression(firstArg) && SOURCES_OBJECT_ACCEPTING_STATIC_OBSERVABLE_CREATORS.includes(operatorName)) {
3349
+ if (firstArg.properties.length === 1 && isProperty(firstArg.properties[0])) {
3350
+ const property = firstArg.properties[0];
3351
+ if (property.value && couldBeObservable(property.value)) {
3352
+ context.report({
3353
+ messageId: "forbidden",
3354
+ node: node.callee,
3355
+ data: {
3356
+ operator: operatorName,
3357
+ inputType: "single-property object"
3358
+ }
3359
+ });
3360
+ }
3361
+ }
3362
+ return;
3363
+ }
3364
+ if (args.length === 1 && couldBeObservable(firstArg)) {
3365
+ context.report({
3366
+ messageId: "forbidden",
3367
+ node: node.callee,
3368
+ data: {
3369
+ operator: operatorName,
3370
+ inputType: "single argument"
3371
+ }
3372
+ });
3373
+ }
3374
+ }
3375
+ const callExpressionVisitors = {};
3376
+ for (const operator of MULTIPLE_OBSERVABLE_ACCEPTING_STATIC_OBSERVABLE_CREATORS) {
3377
+ callExpressionVisitors[`CallExpression[callee.name="${operator}"]`] = (node) => {
3378
+ if (couldBeFromRxjs(node.callee, operator)) {
3379
+ checkCallExpression(node, operator);
3380
+ }
3381
+ };
3382
+ }
3383
+ for (const operator of MULTIPLE_OBSERVABLE_ACCEPTING_STATIC_OBSERVABLE_CREATORS) {
3384
+ callExpressionVisitors[`CallExpression[callee.type="MemberExpression"][callee.property.name="${operator}"]`] = (node) => {
3385
+ const memberExpr = node.callee;
3386
+ if (couldBeFromRxjs(memberExpr.property, operator)) {
3387
+ checkCallExpression(node, operator);
3388
+ }
3389
+ };
3390
+ }
3391
+ return callExpressionVisitors;
3392
+ }
3393
+ });
3394
+
3238
3395
  // src/rules/no-unsafe-catch.ts
3239
3396
  import { stripIndent as stripIndent5 } from "common-tags";
3240
3397
  var defaultOptions12 = [];
@@ -4158,6 +4315,7 @@ var allRules = {
4158
4315
  "no-nested-subscribe": noNestedSubscribeRule,
4159
4316
  "no-redundant-notify": noRedundantNotifyRule,
4160
4317
  "no-sharereplay": noSharereplayRule,
4318
+ "no-sharereplay-before-takeuntil": noSharereplayBeforeTakeuntilRule,
4161
4319
  "no-subclass": noSubclassRule,
4162
4320
  "no-subject-unsubscribe": noSubjectUnsubscribeRule,
4163
4321
  "no-subject-value": noSubjectValueRule,
@@ -4166,6 +4324,7 @@ var allRules = {
4166
4324
  "no-tap": noTapRule,
4167
4325
  "no-topromise": noTopromiseRule,
4168
4326
  "no-unbound-methods": noUnboundMethodsRule,
4327
+ "no-unnecessary-collection": noUnnecessaryCollectionRule,
4169
4328
  "no-unsafe-catch": noUnsafeCatchRule,
4170
4329
  "no-unsafe-first": noUnsafeFirstRule,
4171
4330
  "no-unsafe-subject-next": noUnsafeSubjectNext,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eslint-plugin-rxjs-x",
3
3
  "type": "commonjs",
4
- "version": "0.8.0",
4
+ "version": "0.8.1",
5
5
  "packageManager": "yarn@4.10.3+sha512.c38cafb5c7bb273f3926d04e55e1d8c9dfa7d9c3ea1f36a4868fa028b9e5f72298f0b7f401ad5eb921749eb012eb1c3bb74bf7503df3ee43fd600d14a018266f",
6
6
  "description": "ESLint v9+ plugin for RxJS",
7
7
  "author": "Jason Weinzierl <weinzierljason@gmail.com>",
@@ -78,7 +78,7 @@
78
78
  "@types/common-tags": "^1.8.4",
79
79
  "@types/node": "~18.18.0",
80
80
  "@typescript-eslint/rule-tester": "^8.46.2",
81
- "@typescript/vfs": "^1.6.1",
81
+ "@typescript/vfs": "^1.6.2",
82
82
  "@vitest/coverage-v8": "^3.2.4",
83
83
  "@vitest/eslint-plugin": "^1.4.0",
84
84
  "bumpp": "^10.3.1",