eslint-plugin-rxjs-x 0.8.0 → 0.8.2
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 +49 -47
- package/dist/index.d.mts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +163 -1
- package/dist/index.mjs +163 -1
- package/package.json +2 -2
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
|
|
112
|
-
|
|
|
113
|
-
| [ban-observables](docs/rules/ban-observables.md)
|
|
114
|
-
| [ban-operators](docs/rules/ban-operators.md)
|
|
115
|
-
| [finnish](docs/rules/finnish.md)
|
|
116
|
-
| [just](docs/rules/just.md)
|
|
117
|
-
| [macro](docs/rules/macro.md)
|
|
118
|
-
| [no-async-subscribe](docs/rules/no-async-subscribe.md)
|
|
119
|
-
| [no-compat](docs/rules/no-compat.md)
|
|
120
|
-
| [no-connectable](docs/rules/no-connectable.md)
|
|
121
|
-
| [no-create](docs/rules/no-create.md)
|
|
122
|
-
| [no-cyclic-action](docs/rules/no-cyclic-action.md)
|
|
123
|
-
| [no-explicit-generics](docs/rules/no-explicit-generics.md)
|
|
124
|
-
| [no-exposed-subjects](docs/rules/no-exposed-subjects.md)
|
|
125
|
-
| [no-finnish](docs/rules/no-finnish.md)
|
|
126
|
-
| [no-floating-observables](docs/rules/no-floating-observables.md)
|
|
127
|
-
| [no-ignored-default-value](docs/rules/no-ignored-default-value.md)
|
|
128
|
-
| [no-ignored-error](docs/rules/no-ignored-error.md)
|
|
129
|
-
| [no-ignored-notifier](docs/rules/no-ignored-notifier.md)
|
|
130
|
-
| [no-ignored-replay-buffer](docs/rules/no-ignored-replay-buffer.md)
|
|
131
|
-
| [no-ignored-subscribe](docs/rules/no-ignored-subscribe.md)
|
|
132
|
-
| [no-ignored-subscription](docs/rules/no-ignored-subscription.md)
|
|
133
|
-
| [no-ignored-takewhile-value](docs/rules/no-ignored-takewhile-value.md)
|
|
134
|
-
| [no-implicit-any-catch](docs/rules/no-implicit-any-catch.md)
|
|
135
|
-
| [no-index](docs/rules/no-index.md)
|
|
136
|
-
| [no-internal](docs/rules/no-internal.md)
|
|
137
|
-
| [no-misused-observables](docs/rules/no-misused-observables.md)
|
|
138
|
-
| [no-nested-subscribe](docs/rules/no-nested-subscribe.md)
|
|
139
|
-
| [no-redundant-notify](docs/rules/no-redundant-notify.md)
|
|
140
|
-
| [no-sharereplay](docs/rules/no-sharereplay.md)
|
|
141
|
-
| [no-
|
|
142
|
-
| [no-
|
|
143
|
-
| [no-subject-
|
|
144
|
-
| [no-
|
|
145
|
-
| [no-subscribe-
|
|
146
|
-
| [no-
|
|
147
|
-
| [no-
|
|
148
|
-
| [no-
|
|
149
|
-
| [no-
|
|
150
|
-
| [no-
|
|
151
|
-
| [no-unsafe-
|
|
152
|
-
| [no-unsafe-
|
|
153
|
-
| [no-unsafe-
|
|
154
|
-
| [
|
|
155
|
-
| [
|
|
156
|
-
| [
|
|
157
|
-
| [
|
|
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.
|
|
3
|
+
var version = "0.8.2";
|
|
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",
|
|
@@ -286,6 +288,9 @@ function isProperty(node) {
|
|
|
286
288
|
function isPropertyDefinition(node) {
|
|
287
289
|
return node.type === _utils.AST_NODE_TYPES.PropertyDefinition;
|
|
288
290
|
}
|
|
291
|
+
function isSpreadElement(node) {
|
|
292
|
+
return node.type === _utils.AST_NODE_TYPES.SpreadElement;
|
|
293
|
+
}
|
|
289
294
|
function isUnaryExpression(node) {
|
|
290
295
|
return node.type === _utils.AST_NODE_TYPES.UnaryExpression;
|
|
291
296
|
}
|
|
@@ -403,6 +408,14 @@ var SOURCES_OBJECT_ACCEPTING_STATIC_OBSERVABLE_CREATORS = [
|
|
|
403
408
|
"combineLatest",
|
|
404
409
|
"forkJoin"
|
|
405
410
|
];
|
|
411
|
+
var MULTIPLE_OBSERVABLE_ACCEPTING_STATIC_OBSERVABLE_CREATORS = [
|
|
412
|
+
"combineLatest",
|
|
413
|
+
"forkJoin",
|
|
414
|
+
"merge",
|
|
415
|
+
"zip",
|
|
416
|
+
"concat",
|
|
417
|
+
"race"
|
|
418
|
+
];
|
|
406
419
|
var DEFAULT_UNBOUND_ALLOWED_TYPES = [
|
|
407
420
|
"Signal"
|
|
408
421
|
];
|
|
@@ -2825,6 +2838,65 @@ var noSharereplayRule = ruleCreator({
|
|
|
2825
2838
|
}
|
|
2826
2839
|
});
|
|
2827
2840
|
|
|
2841
|
+
// src/rules/no-sharereplay-before-takeuntil.ts
|
|
2842
|
+
var noSharereplayBeforeTakeuntilRule = ruleCreator({
|
|
2843
|
+
defaultOptions: [],
|
|
2844
|
+
meta: {
|
|
2845
|
+
docs: {
|
|
2846
|
+
description: "Disallow using `shareReplay({ refCount: false })` before `takeUntil`.",
|
|
2847
|
+
recommended: "strict"
|
|
2848
|
+
},
|
|
2849
|
+
messages: {
|
|
2850
|
+
forbidden: "shareReplay before takeUntil is forbidden unless 'refCount: true' is specified."
|
|
2851
|
+
},
|
|
2852
|
+
schema: [],
|
|
2853
|
+
type: "problem"
|
|
2854
|
+
},
|
|
2855
|
+
name: "no-sharereplay-before-takeuntil",
|
|
2856
|
+
create: (context) => {
|
|
2857
|
+
function checkCallExpression(node) {
|
|
2858
|
+
const pipeCallExpression = node.parent;
|
|
2859
|
+
if (!pipeCallExpression.arguments || !isMemberExpression(pipeCallExpression.callee) || !isIdentifier(pipeCallExpression.callee.property) || pipeCallExpression.callee.property.name !== "pipe") {
|
|
2860
|
+
return;
|
|
2861
|
+
}
|
|
2862
|
+
const { isOrderValid, operatorNode: takeUntilNode } = findIsLastOperatorOrderValid(
|
|
2863
|
+
pipeCallExpression,
|
|
2864
|
+
/^takeUntil$/,
|
|
2865
|
+
DEFAULT_VALID_POST_COMPLETION_OPERATORS
|
|
2866
|
+
);
|
|
2867
|
+
if (!isOrderValid || !takeUntilNode) {
|
|
2868
|
+
return;
|
|
2869
|
+
}
|
|
2870
|
+
if (takeUntilNode.range[0] < node.range[0]) {
|
|
2871
|
+
return;
|
|
2872
|
+
}
|
|
2873
|
+
const shareReplayConfig = node.arguments[0];
|
|
2874
|
+
if (!shareReplayConfig || !isObjectExpression(shareReplayConfig)) {
|
|
2875
|
+
context.report({
|
|
2876
|
+
messageId: "forbidden",
|
|
2877
|
+
node: node.callee
|
|
2878
|
+
});
|
|
2879
|
+
return;
|
|
2880
|
+
}
|
|
2881
|
+
const refCountElement = shareReplayConfig.properties.filter(isProperty).find((prop) => isIdentifier(prop.key) && prop.key.name === "refCount");
|
|
2882
|
+
if (!refCountElement || isLiteral(refCountElement.value) && refCountElement.value.value === false) {
|
|
2883
|
+
context.report({
|
|
2884
|
+
messageId: "forbidden",
|
|
2885
|
+
node: node.callee
|
|
2886
|
+
});
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
return {
|
|
2890
|
+
'CallExpression[callee.name="shareReplay"]': (node) => {
|
|
2891
|
+
checkCallExpression(node);
|
|
2892
|
+
},
|
|
2893
|
+
'CallExpression[callee.property.name="shareReplay"]': (node) => {
|
|
2894
|
+
checkCallExpression(node);
|
|
2895
|
+
}
|
|
2896
|
+
};
|
|
2897
|
+
}
|
|
2898
|
+
});
|
|
2899
|
+
|
|
2828
2900
|
// src/rules/no-subclass.ts
|
|
2829
2901
|
var noSubclassRule = ruleCreator({
|
|
2830
2902
|
defaultOptions: [],
|
|
@@ -3235,6 +3307,94 @@ var noUnboundMethodsRule = ruleCreator({
|
|
|
3235
3307
|
}
|
|
3236
3308
|
});
|
|
3237
3309
|
|
|
3310
|
+
// src/rules/no-unnecessary-collection.ts
|
|
3311
|
+
var noUnnecessaryCollectionRule = ruleCreator({
|
|
3312
|
+
defaultOptions: [],
|
|
3313
|
+
meta: {
|
|
3314
|
+
docs: {
|
|
3315
|
+
description: "Disallow unnecessary usage of collection arguments with single values.",
|
|
3316
|
+
recommended: "strict",
|
|
3317
|
+
requiresTypeChecking: false
|
|
3318
|
+
},
|
|
3319
|
+
messages: {
|
|
3320
|
+
forbidden: "Unnecessary {{operator}} with {{inputType}}. Use the observable directly instead."
|
|
3321
|
+
},
|
|
3322
|
+
schema: [],
|
|
3323
|
+
type: "suggestion"
|
|
3324
|
+
},
|
|
3325
|
+
name: "no-unnecessary-collection",
|
|
3326
|
+
create: (context) => {
|
|
3327
|
+
const { couldBeType: couldBeType2, couldBeObservable } = getTypeServices(context);
|
|
3328
|
+
function couldBeFromRxjs(node, operatorName) {
|
|
3329
|
+
return couldBeType2(node, operatorName, { name: /[/\\]rxjs[/\\]/ });
|
|
3330
|
+
}
|
|
3331
|
+
function checkCallExpression(node, operatorName) {
|
|
3332
|
+
const args = node.arguments;
|
|
3333
|
+
if (args.length === 0) {
|
|
3334
|
+
return;
|
|
3335
|
+
}
|
|
3336
|
+
const firstArg = args[0];
|
|
3337
|
+
if (isArrayExpression(firstArg)) {
|
|
3338
|
+
const nonNullElements = firstArg.elements.filter((element) => element !== null);
|
|
3339
|
+
if (nonNullElements.length === 1 && couldBeObservable(nonNullElements[0])) {
|
|
3340
|
+
context.report({
|
|
3341
|
+
messageId: "forbidden",
|
|
3342
|
+
node: node.callee,
|
|
3343
|
+
data: {
|
|
3344
|
+
operator: operatorName,
|
|
3345
|
+
inputType: "single-valued array"
|
|
3346
|
+
}
|
|
3347
|
+
});
|
|
3348
|
+
}
|
|
3349
|
+
return;
|
|
3350
|
+
}
|
|
3351
|
+
if (isObjectExpression(firstArg) && SOURCES_OBJECT_ACCEPTING_STATIC_OBSERVABLE_CREATORS.includes(operatorName)) {
|
|
3352
|
+
if (firstArg.properties.length === 1 && isProperty(firstArg.properties[0])) {
|
|
3353
|
+
const property = firstArg.properties[0];
|
|
3354
|
+
if (property.value && couldBeObservable(property.value)) {
|
|
3355
|
+
context.report({
|
|
3356
|
+
messageId: "forbidden",
|
|
3357
|
+
node: node.callee,
|
|
3358
|
+
data: {
|
|
3359
|
+
operator: operatorName,
|
|
3360
|
+
inputType: "single-property object"
|
|
3361
|
+
}
|
|
3362
|
+
});
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
return;
|
|
3366
|
+
}
|
|
3367
|
+
if (args.length === 1 && !isSpreadElement(firstArg) && couldBeObservable(firstArg)) {
|
|
3368
|
+
context.report({
|
|
3369
|
+
messageId: "forbidden",
|
|
3370
|
+
node: node.callee,
|
|
3371
|
+
data: {
|
|
3372
|
+
operator: operatorName,
|
|
3373
|
+
inputType: "single argument"
|
|
3374
|
+
}
|
|
3375
|
+
});
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
const callExpressionVisitors = {};
|
|
3379
|
+
for (const operator of MULTIPLE_OBSERVABLE_ACCEPTING_STATIC_OBSERVABLE_CREATORS) {
|
|
3380
|
+
callExpressionVisitors[`CallExpression[callee.name="${operator}"]`] = (node) => {
|
|
3381
|
+
if (couldBeFromRxjs(node.callee, operator)) {
|
|
3382
|
+
checkCallExpression(node, operator);
|
|
3383
|
+
}
|
|
3384
|
+
};
|
|
3385
|
+
}
|
|
3386
|
+
for (const operator of MULTIPLE_OBSERVABLE_ACCEPTING_STATIC_OBSERVABLE_CREATORS) {
|
|
3387
|
+
callExpressionVisitors[`CallExpression[callee.type="MemberExpression"][callee.property.name="${operator}"]`] = (node) => {
|
|
3388
|
+
const memberExpr = node.callee;
|
|
3389
|
+
if (couldBeFromRxjs(memberExpr.property, operator)) {
|
|
3390
|
+
checkCallExpression(node, operator);
|
|
3391
|
+
}
|
|
3392
|
+
};
|
|
3393
|
+
}
|
|
3394
|
+
return callExpressionVisitors;
|
|
3395
|
+
}
|
|
3396
|
+
});
|
|
3397
|
+
|
|
3238
3398
|
// src/rules/no-unsafe-catch.ts
|
|
3239
3399
|
|
|
3240
3400
|
var defaultOptions12 = [];
|
|
@@ -4158,6 +4318,7 @@ var allRules = {
|
|
|
4158
4318
|
"no-nested-subscribe": noNestedSubscribeRule,
|
|
4159
4319
|
"no-redundant-notify": noRedundantNotifyRule,
|
|
4160
4320
|
"no-sharereplay": noSharereplayRule,
|
|
4321
|
+
"no-sharereplay-before-takeuntil": noSharereplayBeforeTakeuntilRule,
|
|
4161
4322
|
"no-subclass": noSubclassRule,
|
|
4162
4323
|
"no-subject-unsubscribe": noSubjectUnsubscribeRule,
|
|
4163
4324
|
"no-subject-value": noSubjectValueRule,
|
|
@@ -4166,6 +4327,7 @@ var allRules = {
|
|
|
4166
4327
|
"no-tap": noTapRule,
|
|
4167
4328
|
"no-topromise": noTopromiseRule,
|
|
4168
4329
|
"no-unbound-methods": noUnboundMethodsRule,
|
|
4330
|
+
"no-unnecessary-collection": noUnnecessaryCollectionRule,
|
|
4169
4331
|
"no-unsafe-catch": noUnsafeCatchRule,
|
|
4170
4332
|
"no-unsafe-first": noUnsafeFirstRule,
|
|
4171
4333
|
"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.
|
|
3
|
+
var version = "0.8.2";
|
|
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",
|
|
@@ -286,6 +288,9 @@ function isProperty(node) {
|
|
|
286
288
|
function isPropertyDefinition(node) {
|
|
287
289
|
return node.type === AST_NODE_TYPES.PropertyDefinition;
|
|
288
290
|
}
|
|
291
|
+
function isSpreadElement(node) {
|
|
292
|
+
return node.type === AST_NODE_TYPES.SpreadElement;
|
|
293
|
+
}
|
|
289
294
|
function isUnaryExpression(node) {
|
|
290
295
|
return node.type === AST_NODE_TYPES.UnaryExpression;
|
|
291
296
|
}
|
|
@@ -403,6 +408,14 @@ var SOURCES_OBJECT_ACCEPTING_STATIC_OBSERVABLE_CREATORS = [
|
|
|
403
408
|
"combineLatest",
|
|
404
409
|
"forkJoin"
|
|
405
410
|
];
|
|
411
|
+
var MULTIPLE_OBSERVABLE_ACCEPTING_STATIC_OBSERVABLE_CREATORS = [
|
|
412
|
+
"combineLatest",
|
|
413
|
+
"forkJoin",
|
|
414
|
+
"merge",
|
|
415
|
+
"zip",
|
|
416
|
+
"concat",
|
|
417
|
+
"race"
|
|
418
|
+
];
|
|
406
419
|
var DEFAULT_UNBOUND_ALLOWED_TYPES = [
|
|
407
420
|
"Signal"
|
|
408
421
|
];
|
|
@@ -2825,6 +2838,65 @@ var noSharereplayRule = ruleCreator({
|
|
|
2825
2838
|
}
|
|
2826
2839
|
});
|
|
2827
2840
|
|
|
2841
|
+
// src/rules/no-sharereplay-before-takeuntil.ts
|
|
2842
|
+
var noSharereplayBeforeTakeuntilRule = ruleCreator({
|
|
2843
|
+
defaultOptions: [],
|
|
2844
|
+
meta: {
|
|
2845
|
+
docs: {
|
|
2846
|
+
description: "Disallow using `shareReplay({ refCount: false })` before `takeUntil`.",
|
|
2847
|
+
recommended: "strict"
|
|
2848
|
+
},
|
|
2849
|
+
messages: {
|
|
2850
|
+
forbidden: "shareReplay before takeUntil is forbidden unless 'refCount: true' is specified."
|
|
2851
|
+
},
|
|
2852
|
+
schema: [],
|
|
2853
|
+
type: "problem"
|
|
2854
|
+
},
|
|
2855
|
+
name: "no-sharereplay-before-takeuntil",
|
|
2856
|
+
create: (context) => {
|
|
2857
|
+
function checkCallExpression(node) {
|
|
2858
|
+
const pipeCallExpression = node.parent;
|
|
2859
|
+
if (!pipeCallExpression.arguments || !isMemberExpression(pipeCallExpression.callee) || !isIdentifier(pipeCallExpression.callee.property) || pipeCallExpression.callee.property.name !== "pipe") {
|
|
2860
|
+
return;
|
|
2861
|
+
}
|
|
2862
|
+
const { isOrderValid, operatorNode: takeUntilNode } = findIsLastOperatorOrderValid(
|
|
2863
|
+
pipeCallExpression,
|
|
2864
|
+
/^takeUntil$/,
|
|
2865
|
+
DEFAULT_VALID_POST_COMPLETION_OPERATORS
|
|
2866
|
+
);
|
|
2867
|
+
if (!isOrderValid || !takeUntilNode) {
|
|
2868
|
+
return;
|
|
2869
|
+
}
|
|
2870
|
+
if (takeUntilNode.range[0] < node.range[0]) {
|
|
2871
|
+
return;
|
|
2872
|
+
}
|
|
2873
|
+
const shareReplayConfig = node.arguments[0];
|
|
2874
|
+
if (!shareReplayConfig || !isObjectExpression(shareReplayConfig)) {
|
|
2875
|
+
context.report({
|
|
2876
|
+
messageId: "forbidden",
|
|
2877
|
+
node: node.callee
|
|
2878
|
+
});
|
|
2879
|
+
return;
|
|
2880
|
+
}
|
|
2881
|
+
const refCountElement = shareReplayConfig.properties.filter(isProperty).find((prop) => isIdentifier(prop.key) && prop.key.name === "refCount");
|
|
2882
|
+
if (!refCountElement || isLiteral(refCountElement.value) && refCountElement.value.value === false) {
|
|
2883
|
+
context.report({
|
|
2884
|
+
messageId: "forbidden",
|
|
2885
|
+
node: node.callee
|
|
2886
|
+
});
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
return {
|
|
2890
|
+
'CallExpression[callee.name="shareReplay"]': (node) => {
|
|
2891
|
+
checkCallExpression(node);
|
|
2892
|
+
},
|
|
2893
|
+
'CallExpression[callee.property.name="shareReplay"]': (node) => {
|
|
2894
|
+
checkCallExpression(node);
|
|
2895
|
+
}
|
|
2896
|
+
};
|
|
2897
|
+
}
|
|
2898
|
+
});
|
|
2899
|
+
|
|
2828
2900
|
// src/rules/no-subclass.ts
|
|
2829
2901
|
var noSubclassRule = ruleCreator({
|
|
2830
2902
|
defaultOptions: [],
|
|
@@ -3235,6 +3307,94 @@ var noUnboundMethodsRule = ruleCreator({
|
|
|
3235
3307
|
}
|
|
3236
3308
|
});
|
|
3237
3309
|
|
|
3310
|
+
// src/rules/no-unnecessary-collection.ts
|
|
3311
|
+
var noUnnecessaryCollectionRule = ruleCreator({
|
|
3312
|
+
defaultOptions: [],
|
|
3313
|
+
meta: {
|
|
3314
|
+
docs: {
|
|
3315
|
+
description: "Disallow unnecessary usage of collection arguments with single values.",
|
|
3316
|
+
recommended: "strict",
|
|
3317
|
+
requiresTypeChecking: false
|
|
3318
|
+
},
|
|
3319
|
+
messages: {
|
|
3320
|
+
forbidden: "Unnecessary {{operator}} with {{inputType}}. Use the observable directly instead."
|
|
3321
|
+
},
|
|
3322
|
+
schema: [],
|
|
3323
|
+
type: "suggestion"
|
|
3324
|
+
},
|
|
3325
|
+
name: "no-unnecessary-collection",
|
|
3326
|
+
create: (context) => {
|
|
3327
|
+
const { couldBeType: couldBeType2, couldBeObservable } = getTypeServices(context);
|
|
3328
|
+
function couldBeFromRxjs(node, operatorName) {
|
|
3329
|
+
return couldBeType2(node, operatorName, { name: /[/\\]rxjs[/\\]/ });
|
|
3330
|
+
}
|
|
3331
|
+
function checkCallExpression(node, operatorName) {
|
|
3332
|
+
const args = node.arguments;
|
|
3333
|
+
if (args.length === 0) {
|
|
3334
|
+
return;
|
|
3335
|
+
}
|
|
3336
|
+
const firstArg = args[0];
|
|
3337
|
+
if (isArrayExpression(firstArg)) {
|
|
3338
|
+
const nonNullElements = firstArg.elements.filter((element) => element !== null);
|
|
3339
|
+
if (nonNullElements.length === 1 && couldBeObservable(nonNullElements[0])) {
|
|
3340
|
+
context.report({
|
|
3341
|
+
messageId: "forbidden",
|
|
3342
|
+
node: node.callee,
|
|
3343
|
+
data: {
|
|
3344
|
+
operator: operatorName,
|
|
3345
|
+
inputType: "single-valued array"
|
|
3346
|
+
}
|
|
3347
|
+
});
|
|
3348
|
+
}
|
|
3349
|
+
return;
|
|
3350
|
+
}
|
|
3351
|
+
if (isObjectExpression(firstArg) && SOURCES_OBJECT_ACCEPTING_STATIC_OBSERVABLE_CREATORS.includes(operatorName)) {
|
|
3352
|
+
if (firstArg.properties.length === 1 && isProperty(firstArg.properties[0])) {
|
|
3353
|
+
const property = firstArg.properties[0];
|
|
3354
|
+
if (property.value && couldBeObservable(property.value)) {
|
|
3355
|
+
context.report({
|
|
3356
|
+
messageId: "forbidden",
|
|
3357
|
+
node: node.callee,
|
|
3358
|
+
data: {
|
|
3359
|
+
operator: operatorName,
|
|
3360
|
+
inputType: "single-property object"
|
|
3361
|
+
}
|
|
3362
|
+
});
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
return;
|
|
3366
|
+
}
|
|
3367
|
+
if (args.length === 1 && !isSpreadElement(firstArg) && couldBeObservable(firstArg)) {
|
|
3368
|
+
context.report({
|
|
3369
|
+
messageId: "forbidden",
|
|
3370
|
+
node: node.callee,
|
|
3371
|
+
data: {
|
|
3372
|
+
operator: operatorName,
|
|
3373
|
+
inputType: "single argument"
|
|
3374
|
+
}
|
|
3375
|
+
});
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
const callExpressionVisitors = {};
|
|
3379
|
+
for (const operator of MULTIPLE_OBSERVABLE_ACCEPTING_STATIC_OBSERVABLE_CREATORS) {
|
|
3380
|
+
callExpressionVisitors[`CallExpression[callee.name="${operator}"]`] = (node) => {
|
|
3381
|
+
if (couldBeFromRxjs(node.callee, operator)) {
|
|
3382
|
+
checkCallExpression(node, operator);
|
|
3383
|
+
}
|
|
3384
|
+
};
|
|
3385
|
+
}
|
|
3386
|
+
for (const operator of MULTIPLE_OBSERVABLE_ACCEPTING_STATIC_OBSERVABLE_CREATORS) {
|
|
3387
|
+
callExpressionVisitors[`CallExpression[callee.type="MemberExpression"][callee.property.name="${operator}"]`] = (node) => {
|
|
3388
|
+
const memberExpr = node.callee;
|
|
3389
|
+
if (couldBeFromRxjs(memberExpr.property, operator)) {
|
|
3390
|
+
checkCallExpression(node, operator);
|
|
3391
|
+
}
|
|
3392
|
+
};
|
|
3393
|
+
}
|
|
3394
|
+
return callExpressionVisitors;
|
|
3395
|
+
}
|
|
3396
|
+
});
|
|
3397
|
+
|
|
3238
3398
|
// src/rules/no-unsafe-catch.ts
|
|
3239
3399
|
import { stripIndent as stripIndent5 } from "common-tags";
|
|
3240
3400
|
var defaultOptions12 = [];
|
|
@@ -4158,6 +4318,7 @@ var allRules = {
|
|
|
4158
4318
|
"no-nested-subscribe": noNestedSubscribeRule,
|
|
4159
4319
|
"no-redundant-notify": noRedundantNotifyRule,
|
|
4160
4320
|
"no-sharereplay": noSharereplayRule,
|
|
4321
|
+
"no-sharereplay-before-takeuntil": noSharereplayBeforeTakeuntilRule,
|
|
4161
4322
|
"no-subclass": noSubclassRule,
|
|
4162
4323
|
"no-subject-unsubscribe": noSubjectUnsubscribeRule,
|
|
4163
4324
|
"no-subject-value": noSubjectValueRule,
|
|
@@ -4166,6 +4327,7 @@ var allRules = {
|
|
|
4166
4327
|
"no-tap": noTapRule,
|
|
4167
4328
|
"no-topromise": noTopromiseRule,
|
|
4168
4329
|
"no-unbound-methods": noUnboundMethodsRule,
|
|
4330
|
+
"no-unnecessary-collection": noUnnecessaryCollectionRule,
|
|
4169
4331
|
"no-unsafe-catch": noUnsafeCatchRule,
|
|
4170
4332
|
"no-unsafe-first": noUnsafeFirstRule,
|
|
4171
4333
|
"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.
|
|
4
|
+
"version": "0.8.2",
|
|
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.
|
|
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",
|