eslint-plugin-rxjs-x 0.3.0 → 0.3.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 +3 -2
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +252 -9
- package/dist/index.mjs +260 -17
- package/docs/rules/no-async-subscribe.md +15 -2
- package/docs/rules/no-compat.md +4 -0
- package/docs/rules/no-topromise.md +6 -0
- package/docs/rules/prefer-root-operators.md +32 -0
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ The package includes the following rules.
|
|
|
72
72
|
| [just](docs/rules/just.md) | Require the use of `just` instead of `of`. | | 🔧 | | | |
|
|
73
73
|
| [macro](docs/rules/macro.md) | Require the use of the RxJS Tools Babel macro. | | 🔧 | | | ❌ |
|
|
74
74
|
| [no-async-subscribe](docs/rules/no-async-subscribe.md) | Disallow passing `async` functions to `subscribe`. | ✅ | | | 💭 | |
|
|
75
|
-
| [no-compat](docs/rules/no-compat.md) | Disallow the `rxjs-compat` package. | | | | |
|
|
75
|
+
| [no-compat](docs/rules/no-compat.md) | Disallow the `rxjs-compat` package. | | | | | ❌ |
|
|
76
76
|
| [no-connectable](docs/rules/no-connectable.md) | Disallow operators that return connectable observables. | | | | 💭 | |
|
|
77
77
|
| [no-create](docs/rules/no-create.md) | Disallow the static `Observable.create` function. | ✅ | | | 💭 | |
|
|
78
78
|
| [no-cyclic-action](docs/rules/no-cyclic-action.md) | Disallow cyclic actions in effects and epics. | | | | 💭 | |
|
|
@@ -98,7 +98,7 @@ The package includes the following rules.
|
|
|
98
98
|
| [no-subject-value](docs/rules/no-subject-value.md) | Disallow accessing the `value` property of a `BehaviorSubject` instance. | | | | 💭 | |
|
|
99
99
|
| [no-subscribe-handlers](docs/rules/no-subscribe-handlers.md) | Disallow passing handlers to `subscribe`. | | | | 💭 | |
|
|
100
100
|
| [no-tap](docs/rules/no-tap.md) | Disallow the `tap` operator. | | | | | ❌ |
|
|
101
|
-
| [no-topromise](docs/rules/no-topromise.md) | Disallow use of the `toPromise` method. | | |
|
|
101
|
+
| [no-topromise](docs/rules/no-topromise.md) | Disallow use of the `toPromise` method. | | | 💡 | 💭 | |
|
|
102
102
|
| [no-unbound-methods](docs/rules/no-unbound-methods.md) | Disallow passing unbound methods. | ✅ | | | 💭 | |
|
|
103
103
|
| [no-unsafe-catch](docs/rules/no-unsafe-catch.md) | Disallow unsafe `catchError` usage in effects and epics. | | | | 💭 | |
|
|
104
104
|
| [no-unsafe-first](docs/rules/no-unsafe-first.md) | Disallow unsafe `first`/`take` usage in effects and epics. | | | | 💭 | |
|
|
@@ -106,6 +106,7 @@ The package includes the following rules.
|
|
|
106
106
|
| [no-unsafe-switchmap](docs/rules/no-unsafe-switchmap.md) | Disallow unsafe `switchMap` usage in effects and epics. | | | | 💭 | |
|
|
107
107
|
| [no-unsafe-takeuntil](docs/rules/no-unsafe-takeuntil.md) | Disallow applying operators after `takeUntil`. | ✅ | | | 💭 | |
|
|
108
108
|
| [prefer-observer](docs/rules/prefer-observer.md) | Disallow passing separate handlers to `subscribe` and `tap`. | | 🔧 | 💡 | 💭 | |
|
|
109
|
+
| [prefer-root-operators](docs/rules/prefer-root-operators.md) | Disallow importing operators from `rxjs/operators`. | | 🔧 | 💡 | | |
|
|
109
110
|
| [suffix-subjects](docs/rules/suffix-subjects.md) | Enforce the use of a suffix in subject identifiers. | | | | 💭 | |
|
|
110
111
|
| [throw-error](docs/rules/throw-error.md) | Enforce passing only `Error` values to `throwError`. | | | | 💭 | |
|
|
111
112
|
|
package/dist/index.d.mts
CHANGED
|
@@ -87,7 +87,7 @@ declare const rxjsX: {
|
|
|
87
87
|
'no-subject-value': TSESLint.RuleModule<"forbidden", [], RxjsXRuleDocs, TSESLint.RuleListener>;
|
|
88
88
|
'no-subscribe-handlers': TSESLint.RuleModule<"forbidden", [], RxjsXRuleDocs, TSESLint.RuleListener>;
|
|
89
89
|
'no-tap': TSESLint.RuleModule<"forbidden", [], RxjsXRuleDocs, TSESLint.RuleListener>;
|
|
90
|
-
'no-topromise': TSESLint.RuleModule<"forbidden", [], RxjsXRuleDocs, TSESLint.RuleListener>;
|
|
90
|
+
'no-topromise': TSESLint.RuleModule<"forbidden" | "suggestLastValueFrom" | "suggestFirstValueFrom", [], RxjsXRuleDocs, TSESLint.RuleListener>;
|
|
91
91
|
'no-unbound-methods': TSESLint.RuleModule<"forbidden", [], RxjsXRuleDocs, TSESLint.RuleListener>;
|
|
92
92
|
'no-unsafe-catch': TSESLint.RuleModule<"forbidden", readonly {
|
|
93
93
|
observable?: string;
|
|
@@ -108,6 +108,7 @@ declare const rxjsX: {
|
|
|
108
108
|
'prefer-observer': TSESLint.RuleModule<"forbidden", readonly {
|
|
109
109
|
allowNext?: boolean;
|
|
110
110
|
}[], RxjsXRuleDocs, TSESLint.RuleListener>;
|
|
111
|
+
'prefer-root-operators': TSESLint.RuleModule<"forbidden" | "suggest" | "forbiddenWithoutFix", [], RxjsXRuleDocs, TSESLint.RuleListener>;
|
|
111
112
|
'suffix-subjects': TSESLint.RuleModule<"forbidden", readonly {
|
|
112
113
|
parameters?: boolean;
|
|
113
114
|
properties?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -87,7 +87,7 @@ declare const rxjsX: {
|
|
|
87
87
|
'no-subject-value': TSESLint.RuleModule<"forbidden", [], RxjsXRuleDocs, TSESLint.RuleListener>;
|
|
88
88
|
'no-subscribe-handlers': TSESLint.RuleModule<"forbidden", [], RxjsXRuleDocs, TSESLint.RuleListener>;
|
|
89
89
|
'no-tap': TSESLint.RuleModule<"forbidden", [], RxjsXRuleDocs, TSESLint.RuleListener>;
|
|
90
|
-
'no-topromise': TSESLint.RuleModule<"forbidden", [], RxjsXRuleDocs, TSESLint.RuleListener>;
|
|
90
|
+
'no-topromise': TSESLint.RuleModule<"forbidden" | "suggestLastValueFrom" | "suggestFirstValueFrom", [], RxjsXRuleDocs, TSESLint.RuleListener>;
|
|
91
91
|
'no-unbound-methods': TSESLint.RuleModule<"forbidden", [], RxjsXRuleDocs, TSESLint.RuleListener>;
|
|
92
92
|
'no-unsafe-catch': TSESLint.RuleModule<"forbidden", readonly {
|
|
93
93
|
observable?: string;
|
|
@@ -108,6 +108,7 @@ declare const rxjsX: {
|
|
|
108
108
|
'prefer-observer': TSESLint.RuleModule<"forbidden", readonly {
|
|
109
109
|
allowNext?: boolean;
|
|
110
110
|
}[], RxjsXRuleDocs, TSESLint.RuleListener>;
|
|
111
|
+
'prefer-root-operators': TSESLint.RuleModule<"forbidden" | "suggest" | "forbiddenWithoutFix", [], RxjsXRuleDocs, TSESLint.RuleListener>;
|
|
111
112
|
'suffix-subjects': TSESLint.RuleModule<"forbidden", readonly {
|
|
112
113
|
parameters?: boolean;
|
|
113
114
|
properties?: boolean;
|
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.3.
|
|
3
|
+
var version = "0.3.2";
|
|
4
4
|
|
|
5
5
|
// src/configs/recommended.ts
|
|
6
6
|
var createRecommendedConfig = (plugin2) => ({
|
|
@@ -265,6 +265,15 @@ function isFunctionExpression(node) {
|
|
|
265
265
|
function isIdentifier(node) {
|
|
266
266
|
return node.type === _utils.AST_NODE_TYPES.Identifier;
|
|
267
267
|
}
|
|
268
|
+
function isImportDeclaration(node) {
|
|
269
|
+
return node.type === _utils.AST_NODE_TYPES.ImportDeclaration;
|
|
270
|
+
}
|
|
271
|
+
function isImportNamespaceSpecifier(node) {
|
|
272
|
+
return node.type === _utils.AST_NODE_TYPES.ImportNamespaceSpecifier;
|
|
273
|
+
}
|
|
274
|
+
function isImportSpecifier(node) {
|
|
275
|
+
return node.type === _utils.AST_NODE_TYPES.ImportSpecifier;
|
|
276
|
+
}
|
|
268
277
|
function isLiteral(node) {
|
|
269
278
|
return node.type === _utils.AST_NODE_TYPES.Literal;
|
|
270
279
|
}
|
|
@@ -814,6 +823,7 @@ var noAsyncSubscribeRule = ruleCreator({
|
|
|
814
823
|
var noCompatRule = ruleCreator({
|
|
815
824
|
defaultOptions: [],
|
|
816
825
|
meta: {
|
|
826
|
+
deprecated: true,
|
|
817
827
|
docs: {
|
|
818
828
|
description: "Disallow the `rxjs-compat` package."
|
|
819
829
|
},
|
|
@@ -1370,6 +1380,7 @@ var noIgnoredDefaultValueRule = ruleCreator({
|
|
|
1370
1380
|
});
|
|
1371
1381
|
|
|
1372
1382
|
// src/rules/no-ignored-error.ts
|
|
1383
|
+
|
|
1373
1384
|
var noIgnoredErrorRule = ruleCreator({
|
|
1374
1385
|
defaultOptions: [],
|
|
1375
1386
|
meta: {
|
|
@@ -1385,12 +1396,44 @@ var noIgnoredErrorRule = ruleCreator({
|
|
|
1385
1396
|
},
|
|
1386
1397
|
name: "no-ignored-error",
|
|
1387
1398
|
create: (context) => {
|
|
1399
|
+
const { getTypeAtLocation } = _utils.ESLintUtils.getParserServices(context);
|
|
1388
1400
|
const { couldBeObservable, couldBeFunction: couldBeFunction2 } = getTypeServices(context);
|
|
1401
|
+
function isMissingErrorCallback(callExpression) {
|
|
1402
|
+
if (callExpression.arguments.length >= 2) {
|
|
1403
|
+
return false;
|
|
1404
|
+
}
|
|
1405
|
+
return couldBeFunction2(callExpression.arguments[0]);
|
|
1406
|
+
}
|
|
1407
|
+
function isObjMissingError(arg) {
|
|
1408
|
+
return !arg.properties.some(
|
|
1409
|
+
(property) => isProperty(property) && isIdentifier(property.key) && property.key.name === "error"
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1412
|
+
function isTypeMissingError(arg) {
|
|
1413
|
+
const argType = getTypeAtLocation(arg);
|
|
1414
|
+
return !(argType == null ? void 0 : argType.getProperties().some((p) => p.name === "error"));
|
|
1415
|
+
}
|
|
1416
|
+
function isMissingErrorProperty(callExpression) {
|
|
1417
|
+
if (callExpression.arguments.length !== 1) {
|
|
1418
|
+
return false;
|
|
1419
|
+
}
|
|
1420
|
+
const [arg] = callExpression.arguments;
|
|
1421
|
+
if (isObjectExpression(arg)) {
|
|
1422
|
+
return isObjMissingError(arg);
|
|
1423
|
+
}
|
|
1424
|
+
if (isIdentifier(arg)) {
|
|
1425
|
+
return isTypeMissingError(arg);
|
|
1426
|
+
}
|
|
1427
|
+
if (isMemberExpression(arg) && isIdentifier(arg.property)) {
|
|
1428
|
+
return isTypeMissingError(arg.property);
|
|
1429
|
+
}
|
|
1430
|
+
return false;
|
|
1431
|
+
}
|
|
1389
1432
|
return {
|
|
1390
1433
|
"CallExpression[arguments.length > 0] > MemberExpression > Identifier[name='subscribe']": (node) => {
|
|
1391
1434
|
const memberExpression = node.parent;
|
|
1392
1435
|
const callExpression = memberExpression.parent;
|
|
1393
|
-
if (callExpression
|
|
1436
|
+
if ((isMissingErrorCallback(callExpression) || isMissingErrorProperty(callExpression)) && couldBeObservable(memberExpression.object)) {
|
|
1394
1437
|
context.report({
|
|
1395
1438
|
messageId: "forbidden",
|
|
1396
1439
|
node
|
|
@@ -2294,8 +2337,11 @@ var noTopromiseRule = ruleCreator({
|
|
|
2294
2337
|
description: "Disallow use of the `toPromise` method.",
|
|
2295
2338
|
requiresTypeChecking: true
|
|
2296
2339
|
},
|
|
2340
|
+
hasSuggestions: true,
|
|
2297
2341
|
messages: {
|
|
2298
|
-
forbidden: "The toPromise method is forbidden."
|
|
2342
|
+
forbidden: "The toPromise method is forbidden.",
|
|
2343
|
+
suggestLastValueFrom: "Use lastValueFrom instead.",
|
|
2344
|
+
suggestFirstValueFrom: "Use firstValueFrom instead."
|
|
2299
2345
|
},
|
|
2300
2346
|
schema: [],
|
|
2301
2347
|
type: "problem"
|
|
@@ -2303,14 +2349,74 @@ var noTopromiseRule = ruleCreator({
|
|
|
2303
2349
|
name: "no-topromise",
|
|
2304
2350
|
create: (context) => {
|
|
2305
2351
|
const { couldBeObservable } = getTypeServices(context);
|
|
2352
|
+
function getQuote(raw) {
|
|
2353
|
+
const match = /^\s*('|")/.exec(raw);
|
|
2354
|
+
if (!match) {
|
|
2355
|
+
return void 0;
|
|
2356
|
+
}
|
|
2357
|
+
const [, quote] = match;
|
|
2358
|
+
return quote;
|
|
2359
|
+
}
|
|
2360
|
+
function createFix(conversion, callExpression, observableNode, importDeclarations) {
|
|
2361
|
+
return function* fix(fixer) {
|
|
2362
|
+
var _a, _b, _c;
|
|
2363
|
+
let namespace = "";
|
|
2364
|
+
let functionName = conversion;
|
|
2365
|
+
const rxjsImportDeclaration = importDeclarations.find((node) => node.source.value === "rxjs");
|
|
2366
|
+
if ((_a = rxjsImportDeclaration == null ? void 0 : rxjsImportDeclaration.specifiers) == null ? void 0 : _a.every(isImportNamespaceSpecifier)) {
|
|
2367
|
+
namespace = rxjsImportDeclaration.specifiers[0].local.name + ".";
|
|
2368
|
+
} else if ((_b = rxjsImportDeclaration == null ? void 0 : rxjsImportDeclaration.specifiers) == null ? void 0 : _b.every(isImportSpecifier)) {
|
|
2369
|
+
const { specifiers } = rxjsImportDeclaration;
|
|
2370
|
+
const existingSpecifier = specifiers.find((node) => (isIdentifier(node.imported) ? node.imported.name : node.imported.value) === functionName);
|
|
2371
|
+
if (existingSpecifier) {
|
|
2372
|
+
functionName = existingSpecifier.local.name;
|
|
2373
|
+
} else {
|
|
2374
|
+
const lastSpecifier = specifiers[specifiers.length - 1];
|
|
2375
|
+
yield fixer.insertTextAfter(lastSpecifier, `, ${functionName}`);
|
|
2376
|
+
}
|
|
2377
|
+
} else if (importDeclarations.length) {
|
|
2378
|
+
const lastImport = importDeclarations[importDeclarations.length - 1];
|
|
2379
|
+
const quote = (_c = getQuote(lastImport.source.raw)) != null ? _c : '"';
|
|
2380
|
+
yield fixer.insertTextAfter(
|
|
2381
|
+
importDeclarations[importDeclarations.length - 1],
|
|
2382
|
+
`
|
|
2383
|
+
import { ${functionName} } from ${quote}rxjs${quote};`
|
|
2384
|
+
);
|
|
2385
|
+
} else {
|
|
2386
|
+
console.warn("No import declarations found. Unable to suggest a fix.");
|
|
2387
|
+
return;
|
|
2388
|
+
}
|
|
2389
|
+
yield fixer.replaceText(
|
|
2390
|
+
callExpression,
|
|
2391
|
+
`${namespace}${functionName}(${context.sourceCode.getText(observableNode)})`
|
|
2392
|
+
);
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2306
2395
|
return {
|
|
2307
|
-
[`
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2396
|
+
[`CallExpression[callee.property.name="toPromise"]`]: (node) => {
|
|
2397
|
+
const memberExpression = node.callee;
|
|
2398
|
+
if (!couldBeObservable(memberExpression.object)) {
|
|
2399
|
+
return;
|
|
2400
|
+
}
|
|
2401
|
+
const { body } = context.sourceCode.ast;
|
|
2402
|
+
const importDeclarations = body.filter(isImportDeclaration);
|
|
2403
|
+
if (!importDeclarations.length) {
|
|
2404
|
+
return;
|
|
2313
2405
|
}
|
|
2406
|
+
context.report({
|
|
2407
|
+
messageId: "forbidden",
|
|
2408
|
+
node: memberExpression.property,
|
|
2409
|
+
suggest: [
|
|
2410
|
+
{
|
|
2411
|
+
messageId: "suggestLastValueFrom",
|
|
2412
|
+
fix: createFix("lastValueFrom", node, memberExpression.object, importDeclarations)
|
|
2413
|
+
},
|
|
2414
|
+
{
|
|
2415
|
+
messageId: "suggestFirstValueFrom",
|
|
2416
|
+
fix: createFix("firstValueFrom", node, memberExpression.object, importDeclarations)
|
|
2417
|
+
}
|
|
2418
|
+
]
|
|
2419
|
+
});
|
|
2314
2420
|
}
|
|
2315
2421
|
};
|
|
2316
2422
|
}
|
|
@@ -2894,6 +3000,142 @@ function isValidArgText(argText) {
|
|
|
2894
3000
|
return argText && argText !== "undefined" && argText !== "null";
|
|
2895
3001
|
}
|
|
2896
3002
|
|
|
3003
|
+
// src/rules/prefer-root-operators.ts
|
|
3004
|
+
var RENAMED_OPERATORS = {
|
|
3005
|
+
combineLatest: "combineLatestWith",
|
|
3006
|
+
concat: "concatWith",
|
|
3007
|
+
merge: "mergeWith",
|
|
3008
|
+
onErrorResumeNext: "onErrorResumeNextWith",
|
|
3009
|
+
race: "raceWith",
|
|
3010
|
+
zip: "zipWith"
|
|
3011
|
+
};
|
|
3012
|
+
var DEPRECATED_OPERATORS = [
|
|
3013
|
+
"partition"
|
|
3014
|
+
];
|
|
3015
|
+
var preferRootOperatorsRule = ruleCreator({
|
|
3016
|
+
defaultOptions: [],
|
|
3017
|
+
meta: {
|
|
3018
|
+
docs: {
|
|
3019
|
+
description: "Disallow importing operators from `rxjs/operators`."
|
|
3020
|
+
},
|
|
3021
|
+
fixable: "code",
|
|
3022
|
+
hasSuggestions: true,
|
|
3023
|
+
messages: {
|
|
3024
|
+
forbidden: "RxJS imports from `rxjs/operators` are forbidden; import from `rxjs` instead.",
|
|
3025
|
+
forbiddenWithoutFix: "RxJS imports from `rxjs/operators` are forbidden; import from `rxjs` instead. Note some operators may have been renamed or deprecated.",
|
|
3026
|
+
suggest: "Replace with import from `rxjs`."
|
|
3027
|
+
},
|
|
3028
|
+
schema: [],
|
|
3029
|
+
type: "suggestion"
|
|
3030
|
+
},
|
|
3031
|
+
name: "prefer-root-operators",
|
|
3032
|
+
create: (context) => {
|
|
3033
|
+
function getQuote(raw) {
|
|
3034
|
+
const match = /^\s*('|")/.exec(raw);
|
|
3035
|
+
if (!match) {
|
|
3036
|
+
return void 0;
|
|
3037
|
+
}
|
|
3038
|
+
const [, quote] = match;
|
|
3039
|
+
return quote;
|
|
3040
|
+
}
|
|
3041
|
+
function getSourceReplacement(rawLocation) {
|
|
3042
|
+
const quote = getQuote(rawLocation);
|
|
3043
|
+
if (!quote) {
|
|
3044
|
+
return void 0;
|
|
3045
|
+
}
|
|
3046
|
+
if (/^['"]rxjs\/operators/.test(rawLocation)) {
|
|
3047
|
+
return `${quote}rxjs${quote}`;
|
|
3048
|
+
}
|
|
3049
|
+
return void 0;
|
|
3050
|
+
}
|
|
3051
|
+
function hasDeprecatedOperators(specifiers) {
|
|
3052
|
+
return !!(specifiers == null ? void 0 : specifiers.some((s) => DEPRECATED_OPERATORS.includes(getName(getOperatorNode(s)))));
|
|
3053
|
+
}
|
|
3054
|
+
function getName(node) {
|
|
3055
|
+
return isIdentifier(node) ? node.name : node.value;
|
|
3056
|
+
}
|
|
3057
|
+
function getOperatorNode(node) {
|
|
3058
|
+
return isImportSpecifier(node) ? node.imported : node.local;
|
|
3059
|
+
}
|
|
3060
|
+
function getAliasNode(node) {
|
|
3061
|
+
return isImportSpecifier(node) ? node.local : node.exported;
|
|
3062
|
+
}
|
|
3063
|
+
function getOperatorReplacement(name2) {
|
|
3064
|
+
return RENAMED_OPERATORS[name2];
|
|
3065
|
+
}
|
|
3066
|
+
function isNodesEqual(a, b) {
|
|
3067
|
+
return a.range[0] === b.range[0] && a.range[1] === b.range[1];
|
|
3068
|
+
}
|
|
3069
|
+
function createFix(source, replacement, specifiers) {
|
|
3070
|
+
return function* fix(fixer) {
|
|
3071
|
+
yield fixer.replaceText(source, replacement);
|
|
3072
|
+
for (const specifier of specifiers) {
|
|
3073
|
+
const operatorNode = getOperatorNode(specifier);
|
|
3074
|
+
const operatorName = getName(operatorNode);
|
|
3075
|
+
const operatorReplacement = getOperatorReplacement(operatorName);
|
|
3076
|
+
if (!operatorReplacement) {
|
|
3077
|
+
continue;
|
|
3078
|
+
}
|
|
3079
|
+
const aliasNode = getAliasNode(specifier);
|
|
3080
|
+
if (isNodesEqual(aliasNode, operatorNode)) {
|
|
3081
|
+
yield fixer.insertTextBefore(operatorNode, operatorReplacement + " as ");
|
|
3082
|
+
} else if (isIdentifier(operatorNode)) {
|
|
3083
|
+
yield fixer.replaceText(operatorNode, operatorReplacement);
|
|
3084
|
+
} else {
|
|
3085
|
+
const quote = getQuote(operatorNode.raw);
|
|
3086
|
+
if (!quote) {
|
|
3087
|
+
continue;
|
|
3088
|
+
}
|
|
3089
|
+
yield fixer.replaceText(operatorNode, quote + operatorReplacement + quote);
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
};
|
|
3093
|
+
}
|
|
3094
|
+
function reportNode(source, specifiers) {
|
|
3095
|
+
const replacement = getSourceReplacement(source.raw);
|
|
3096
|
+
if (!replacement || hasDeprecatedOperators(specifiers)) {
|
|
3097
|
+
context.report({
|
|
3098
|
+
messageId: "forbiddenWithoutFix",
|
|
3099
|
+
node: source
|
|
3100
|
+
});
|
|
3101
|
+
return;
|
|
3102
|
+
}
|
|
3103
|
+
if (!specifiers) {
|
|
3104
|
+
context.report({
|
|
3105
|
+
messageId: "forbiddenWithoutFix",
|
|
3106
|
+
node: source,
|
|
3107
|
+
suggest: [{ messageId: "suggest", fix: (fixer) => fixer.replaceText(source, replacement) }]
|
|
3108
|
+
});
|
|
3109
|
+
return;
|
|
3110
|
+
}
|
|
3111
|
+
const fix = createFix(source, replacement, specifiers);
|
|
3112
|
+
context.report({
|
|
3113
|
+
fix,
|
|
3114
|
+
messageId: "forbidden",
|
|
3115
|
+
node: source,
|
|
3116
|
+
suggest: [{ messageId: "suggest", fix }]
|
|
3117
|
+
});
|
|
3118
|
+
}
|
|
3119
|
+
return {
|
|
3120
|
+
'ImportDeclaration[source.value="rxjs/operators"]': (node) => {
|
|
3121
|
+
const specifiers = node.specifiers.length && node.specifiers.every((importClause) => isImportSpecifier(importClause)) ? node.specifiers : void 0;
|
|
3122
|
+
reportNode(node.source, specifiers);
|
|
3123
|
+
},
|
|
3124
|
+
'ImportExpression[source.value="rxjs/operators"]': (node) => {
|
|
3125
|
+
if (isLiteral(node.source)) {
|
|
3126
|
+
reportNode(node.source);
|
|
3127
|
+
}
|
|
3128
|
+
},
|
|
3129
|
+
'ExportNamedDeclaration[source.value="rxjs/operators"]': (node) => {
|
|
3130
|
+
reportNode(node.source, node.specifiers);
|
|
3131
|
+
},
|
|
3132
|
+
'ExportAllDeclaration[source.value="rxjs/operators"]': (node) => {
|
|
3133
|
+
reportNode(node.source);
|
|
3134
|
+
}
|
|
3135
|
+
};
|
|
3136
|
+
}
|
|
3137
|
+
});
|
|
3138
|
+
|
|
2897
3139
|
// src/rules/suffix-subjects.ts
|
|
2898
3140
|
|
|
2899
3141
|
var defaultOptions13 = [];
|
|
@@ -3211,6 +3453,7 @@ var plugin = {
|
|
|
3211
3453
|
"no-unsafe-switchmap": noUnsafeSwitchmapRule,
|
|
3212
3454
|
"no-unsafe-takeuntil": noUnsafeTakeuntilRule,
|
|
3213
3455
|
"prefer-observer": preferObserverRule,
|
|
3456
|
+
"prefer-root-operators": preferRootOperatorsRule,
|
|
3214
3457
|
"suffix-subjects": suffixSubjectsRule,
|
|
3215
3458
|
"throw-error": throwErrorRule
|
|
3216
3459
|
}
|
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.3.
|
|
3
|
+
var version = "0.3.2";
|
|
4
4
|
|
|
5
5
|
// src/configs/recommended.ts
|
|
6
6
|
var createRecommendedConfig = (plugin2) => ({
|
|
@@ -265,6 +265,15 @@ function isFunctionExpression(node) {
|
|
|
265
265
|
function isIdentifier(node) {
|
|
266
266
|
return node.type === AST_NODE_TYPES2.Identifier;
|
|
267
267
|
}
|
|
268
|
+
function isImportDeclaration(node) {
|
|
269
|
+
return node.type === AST_NODE_TYPES2.ImportDeclaration;
|
|
270
|
+
}
|
|
271
|
+
function isImportNamespaceSpecifier(node) {
|
|
272
|
+
return node.type === AST_NODE_TYPES2.ImportNamespaceSpecifier;
|
|
273
|
+
}
|
|
274
|
+
function isImportSpecifier(node) {
|
|
275
|
+
return node.type === AST_NODE_TYPES2.ImportSpecifier;
|
|
276
|
+
}
|
|
268
277
|
function isLiteral(node) {
|
|
269
278
|
return node.type === AST_NODE_TYPES2.Literal;
|
|
270
279
|
}
|
|
@@ -814,6 +823,7 @@ var noAsyncSubscribeRule = ruleCreator({
|
|
|
814
823
|
var noCompatRule = ruleCreator({
|
|
815
824
|
defaultOptions: [],
|
|
816
825
|
meta: {
|
|
826
|
+
deprecated: true,
|
|
817
827
|
docs: {
|
|
818
828
|
description: "Disallow the `rxjs-compat` package."
|
|
819
829
|
},
|
|
@@ -1370,6 +1380,7 @@ var noIgnoredDefaultValueRule = ruleCreator({
|
|
|
1370
1380
|
});
|
|
1371
1381
|
|
|
1372
1382
|
// src/rules/no-ignored-error.ts
|
|
1383
|
+
import { ESLintUtils as ESLintUtils7 } from "@typescript-eslint/utils";
|
|
1373
1384
|
var noIgnoredErrorRule = ruleCreator({
|
|
1374
1385
|
defaultOptions: [],
|
|
1375
1386
|
meta: {
|
|
@@ -1385,12 +1396,44 @@ var noIgnoredErrorRule = ruleCreator({
|
|
|
1385
1396
|
},
|
|
1386
1397
|
name: "no-ignored-error",
|
|
1387
1398
|
create: (context) => {
|
|
1399
|
+
const { getTypeAtLocation } = ESLintUtils7.getParserServices(context);
|
|
1388
1400
|
const { couldBeObservable, couldBeFunction: couldBeFunction2 } = getTypeServices(context);
|
|
1401
|
+
function isMissingErrorCallback(callExpression) {
|
|
1402
|
+
if (callExpression.arguments.length >= 2) {
|
|
1403
|
+
return false;
|
|
1404
|
+
}
|
|
1405
|
+
return couldBeFunction2(callExpression.arguments[0]);
|
|
1406
|
+
}
|
|
1407
|
+
function isObjMissingError(arg) {
|
|
1408
|
+
return !arg.properties.some(
|
|
1409
|
+
(property) => isProperty(property) && isIdentifier(property.key) && property.key.name === "error"
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1412
|
+
function isTypeMissingError(arg) {
|
|
1413
|
+
const argType = getTypeAtLocation(arg);
|
|
1414
|
+
return !(argType == null ? void 0 : argType.getProperties().some((p) => p.name === "error"));
|
|
1415
|
+
}
|
|
1416
|
+
function isMissingErrorProperty(callExpression) {
|
|
1417
|
+
if (callExpression.arguments.length !== 1) {
|
|
1418
|
+
return false;
|
|
1419
|
+
}
|
|
1420
|
+
const [arg] = callExpression.arguments;
|
|
1421
|
+
if (isObjectExpression(arg)) {
|
|
1422
|
+
return isObjMissingError(arg);
|
|
1423
|
+
}
|
|
1424
|
+
if (isIdentifier(arg)) {
|
|
1425
|
+
return isTypeMissingError(arg);
|
|
1426
|
+
}
|
|
1427
|
+
if (isMemberExpression(arg) && isIdentifier(arg.property)) {
|
|
1428
|
+
return isTypeMissingError(arg.property);
|
|
1429
|
+
}
|
|
1430
|
+
return false;
|
|
1431
|
+
}
|
|
1389
1432
|
return {
|
|
1390
1433
|
"CallExpression[arguments.length > 0] > MemberExpression > Identifier[name='subscribe']": (node) => {
|
|
1391
1434
|
const memberExpression = node.parent;
|
|
1392
1435
|
const callExpression = memberExpression.parent;
|
|
1393
|
-
if (callExpression
|
|
1436
|
+
if ((isMissingErrorCallback(callExpression) || isMissingErrorProperty(callExpression)) && couldBeObservable(memberExpression.object)) {
|
|
1394
1437
|
context.report({
|
|
1395
1438
|
messageId: "forbidden",
|
|
1396
1439
|
node
|
|
@@ -2294,8 +2337,11 @@ var noTopromiseRule = ruleCreator({
|
|
|
2294
2337
|
description: "Disallow use of the `toPromise` method.",
|
|
2295
2338
|
requiresTypeChecking: true
|
|
2296
2339
|
},
|
|
2340
|
+
hasSuggestions: true,
|
|
2297
2341
|
messages: {
|
|
2298
|
-
forbidden: "The toPromise method is forbidden."
|
|
2342
|
+
forbidden: "The toPromise method is forbidden.",
|
|
2343
|
+
suggestLastValueFrom: "Use lastValueFrom instead.",
|
|
2344
|
+
suggestFirstValueFrom: "Use firstValueFrom instead."
|
|
2299
2345
|
},
|
|
2300
2346
|
schema: [],
|
|
2301
2347
|
type: "problem"
|
|
@@ -2303,21 +2349,81 @@ var noTopromiseRule = ruleCreator({
|
|
|
2303
2349
|
name: "no-topromise",
|
|
2304
2350
|
create: (context) => {
|
|
2305
2351
|
const { couldBeObservable } = getTypeServices(context);
|
|
2352
|
+
function getQuote(raw) {
|
|
2353
|
+
const match = /^\s*('|")/.exec(raw);
|
|
2354
|
+
if (!match) {
|
|
2355
|
+
return void 0;
|
|
2356
|
+
}
|
|
2357
|
+
const [, quote] = match;
|
|
2358
|
+
return quote;
|
|
2359
|
+
}
|
|
2360
|
+
function createFix(conversion, callExpression, observableNode, importDeclarations) {
|
|
2361
|
+
return function* fix(fixer) {
|
|
2362
|
+
var _a, _b, _c;
|
|
2363
|
+
let namespace = "";
|
|
2364
|
+
let functionName = conversion;
|
|
2365
|
+
const rxjsImportDeclaration = importDeclarations.find((node) => node.source.value === "rxjs");
|
|
2366
|
+
if ((_a = rxjsImportDeclaration == null ? void 0 : rxjsImportDeclaration.specifiers) == null ? void 0 : _a.every(isImportNamespaceSpecifier)) {
|
|
2367
|
+
namespace = rxjsImportDeclaration.specifiers[0].local.name + ".";
|
|
2368
|
+
} else if ((_b = rxjsImportDeclaration == null ? void 0 : rxjsImportDeclaration.specifiers) == null ? void 0 : _b.every(isImportSpecifier)) {
|
|
2369
|
+
const { specifiers } = rxjsImportDeclaration;
|
|
2370
|
+
const existingSpecifier = specifiers.find((node) => (isIdentifier(node.imported) ? node.imported.name : node.imported.value) === functionName);
|
|
2371
|
+
if (existingSpecifier) {
|
|
2372
|
+
functionName = existingSpecifier.local.name;
|
|
2373
|
+
} else {
|
|
2374
|
+
const lastSpecifier = specifiers[specifiers.length - 1];
|
|
2375
|
+
yield fixer.insertTextAfter(lastSpecifier, `, ${functionName}`);
|
|
2376
|
+
}
|
|
2377
|
+
} else if (importDeclarations.length) {
|
|
2378
|
+
const lastImport = importDeclarations[importDeclarations.length - 1];
|
|
2379
|
+
const quote = (_c = getQuote(lastImport.source.raw)) != null ? _c : '"';
|
|
2380
|
+
yield fixer.insertTextAfter(
|
|
2381
|
+
importDeclarations[importDeclarations.length - 1],
|
|
2382
|
+
`
|
|
2383
|
+
import { ${functionName} } from ${quote}rxjs${quote};`
|
|
2384
|
+
);
|
|
2385
|
+
} else {
|
|
2386
|
+
console.warn("No import declarations found. Unable to suggest a fix.");
|
|
2387
|
+
return;
|
|
2388
|
+
}
|
|
2389
|
+
yield fixer.replaceText(
|
|
2390
|
+
callExpression,
|
|
2391
|
+
`${namespace}${functionName}(${context.sourceCode.getText(observableNode)})`
|
|
2392
|
+
);
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2306
2395
|
return {
|
|
2307
|
-
[`
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
node: node.property
|
|
2312
|
-
});
|
|
2396
|
+
[`CallExpression[callee.property.name="toPromise"]`]: (node) => {
|
|
2397
|
+
const memberExpression = node.callee;
|
|
2398
|
+
if (!couldBeObservable(memberExpression.object)) {
|
|
2399
|
+
return;
|
|
2313
2400
|
}
|
|
2401
|
+
const { body } = context.sourceCode.ast;
|
|
2402
|
+
const importDeclarations = body.filter(isImportDeclaration);
|
|
2403
|
+
if (!importDeclarations.length) {
|
|
2404
|
+
return;
|
|
2405
|
+
}
|
|
2406
|
+
context.report({
|
|
2407
|
+
messageId: "forbidden",
|
|
2408
|
+
node: memberExpression.property,
|
|
2409
|
+
suggest: [
|
|
2410
|
+
{
|
|
2411
|
+
messageId: "suggestLastValueFrom",
|
|
2412
|
+
fix: createFix("lastValueFrom", node, memberExpression.object, importDeclarations)
|
|
2413
|
+
},
|
|
2414
|
+
{
|
|
2415
|
+
messageId: "suggestFirstValueFrom",
|
|
2416
|
+
fix: createFix("firstValueFrom", node, memberExpression.object, importDeclarations)
|
|
2417
|
+
}
|
|
2418
|
+
]
|
|
2419
|
+
});
|
|
2314
2420
|
}
|
|
2315
2421
|
};
|
|
2316
2422
|
}
|
|
2317
2423
|
});
|
|
2318
2424
|
|
|
2319
2425
|
// src/rules/no-unbound-methods.ts
|
|
2320
|
-
import { ESLintUtils as
|
|
2426
|
+
import { ESLintUtils as ESLintUtils8 } from "@typescript-eslint/utils";
|
|
2321
2427
|
var noUnboundMethodsRule = ruleCreator({
|
|
2322
2428
|
defaultOptions: [],
|
|
2323
2429
|
meta: {
|
|
@@ -2334,7 +2440,7 @@ var noUnboundMethodsRule = ruleCreator({
|
|
|
2334
2440
|
},
|
|
2335
2441
|
name: "no-unbound-methods",
|
|
2336
2442
|
create: (context) => {
|
|
2337
|
-
const { getTypeAtLocation } =
|
|
2443
|
+
const { getTypeAtLocation } = ESLintUtils8.getParserServices(context);
|
|
2338
2444
|
const { couldBeObservable, couldBeSubscription } = getTypeServices(context);
|
|
2339
2445
|
const nodeMap = /* @__PURE__ */ new WeakMap();
|
|
2340
2446
|
function mapArguments(node) {
|
|
@@ -2512,7 +2618,7 @@ var noUnsafeFirstRule = ruleCreator({
|
|
|
2512
2618
|
});
|
|
2513
2619
|
|
|
2514
2620
|
// src/rules/no-unsafe-subject-next.ts
|
|
2515
|
-
import { ESLintUtils as
|
|
2621
|
+
import { ESLintUtils as ESLintUtils9 } from "@typescript-eslint/utils";
|
|
2516
2622
|
import * as tsutils2 from "ts-api-utils";
|
|
2517
2623
|
import ts6 from "typescript";
|
|
2518
2624
|
var noUnsafeSubjectNext = ruleCreator({
|
|
@@ -2531,7 +2637,7 @@ var noUnsafeSubjectNext = ruleCreator({
|
|
|
2531
2637
|
},
|
|
2532
2638
|
name: "no-unsafe-subject-next",
|
|
2533
2639
|
create: (context) => {
|
|
2534
|
-
const { getTypeAtLocation, program } =
|
|
2640
|
+
const { getTypeAtLocation, program } = ESLintUtils9.getParserServices(context);
|
|
2535
2641
|
const typeChecker = program.getTypeChecker();
|
|
2536
2642
|
return {
|
|
2537
2643
|
[`CallExpression[callee.property.name='next']`]: (node) => {
|
|
@@ -2894,8 +3000,144 @@ function isValidArgText(argText) {
|
|
|
2894
3000
|
return argText && argText !== "undefined" && argText !== "null";
|
|
2895
3001
|
}
|
|
2896
3002
|
|
|
3003
|
+
// src/rules/prefer-root-operators.ts
|
|
3004
|
+
var RENAMED_OPERATORS = {
|
|
3005
|
+
combineLatest: "combineLatestWith",
|
|
3006
|
+
concat: "concatWith",
|
|
3007
|
+
merge: "mergeWith",
|
|
3008
|
+
onErrorResumeNext: "onErrorResumeNextWith",
|
|
3009
|
+
race: "raceWith",
|
|
3010
|
+
zip: "zipWith"
|
|
3011
|
+
};
|
|
3012
|
+
var DEPRECATED_OPERATORS = [
|
|
3013
|
+
"partition"
|
|
3014
|
+
];
|
|
3015
|
+
var preferRootOperatorsRule = ruleCreator({
|
|
3016
|
+
defaultOptions: [],
|
|
3017
|
+
meta: {
|
|
3018
|
+
docs: {
|
|
3019
|
+
description: "Disallow importing operators from `rxjs/operators`."
|
|
3020
|
+
},
|
|
3021
|
+
fixable: "code",
|
|
3022
|
+
hasSuggestions: true,
|
|
3023
|
+
messages: {
|
|
3024
|
+
forbidden: "RxJS imports from `rxjs/operators` are forbidden; import from `rxjs` instead.",
|
|
3025
|
+
forbiddenWithoutFix: "RxJS imports from `rxjs/operators` are forbidden; import from `rxjs` instead. Note some operators may have been renamed or deprecated.",
|
|
3026
|
+
suggest: "Replace with import from `rxjs`."
|
|
3027
|
+
},
|
|
3028
|
+
schema: [],
|
|
3029
|
+
type: "suggestion"
|
|
3030
|
+
},
|
|
3031
|
+
name: "prefer-root-operators",
|
|
3032
|
+
create: (context) => {
|
|
3033
|
+
function getQuote(raw) {
|
|
3034
|
+
const match = /^\s*('|")/.exec(raw);
|
|
3035
|
+
if (!match) {
|
|
3036
|
+
return void 0;
|
|
3037
|
+
}
|
|
3038
|
+
const [, quote] = match;
|
|
3039
|
+
return quote;
|
|
3040
|
+
}
|
|
3041
|
+
function getSourceReplacement(rawLocation) {
|
|
3042
|
+
const quote = getQuote(rawLocation);
|
|
3043
|
+
if (!quote) {
|
|
3044
|
+
return void 0;
|
|
3045
|
+
}
|
|
3046
|
+
if (/^['"]rxjs\/operators/.test(rawLocation)) {
|
|
3047
|
+
return `${quote}rxjs${quote}`;
|
|
3048
|
+
}
|
|
3049
|
+
return void 0;
|
|
3050
|
+
}
|
|
3051
|
+
function hasDeprecatedOperators(specifiers) {
|
|
3052
|
+
return !!(specifiers == null ? void 0 : specifiers.some((s) => DEPRECATED_OPERATORS.includes(getName(getOperatorNode(s)))));
|
|
3053
|
+
}
|
|
3054
|
+
function getName(node) {
|
|
3055
|
+
return isIdentifier(node) ? node.name : node.value;
|
|
3056
|
+
}
|
|
3057
|
+
function getOperatorNode(node) {
|
|
3058
|
+
return isImportSpecifier(node) ? node.imported : node.local;
|
|
3059
|
+
}
|
|
3060
|
+
function getAliasNode(node) {
|
|
3061
|
+
return isImportSpecifier(node) ? node.local : node.exported;
|
|
3062
|
+
}
|
|
3063
|
+
function getOperatorReplacement(name2) {
|
|
3064
|
+
return RENAMED_OPERATORS[name2];
|
|
3065
|
+
}
|
|
3066
|
+
function isNodesEqual(a, b) {
|
|
3067
|
+
return a.range[0] === b.range[0] && a.range[1] === b.range[1];
|
|
3068
|
+
}
|
|
3069
|
+
function createFix(source, replacement, specifiers) {
|
|
3070
|
+
return function* fix(fixer) {
|
|
3071
|
+
yield fixer.replaceText(source, replacement);
|
|
3072
|
+
for (const specifier of specifiers) {
|
|
3073
|
+
const operatorNode = getOperatorNode(specifier);
|
|
3074
|
+
const operatorName = getName(operatorNode);
|
|
3075
|
+
const operatorReplacement = getOperatorReplacement(operatorName);
|
|
3076
|
+
if (!operatorReplacement) {
|
|
3077
|
+
continue;
|
|
3078
|
+
}
|
|
3079
|
+
const aliasNode = getAliasNode(specifier);
|
|
3080
|
+
if (isNodesEqual(aliasNode, operatorNode)) {
|
|
3081
|
+
yield fixer.insertTextBefore(operatorNode, operatorReplacement + " as ");
|
|
3082
|
+
} else if (isIdentifier(operatorNode)) {
|
|
3083
|
+
yield fixer.replaceText(operatorNode, operatorReplacement);
|
|
3084
|
+
} else {
|
|
3085
|
+
const quote = getQuote(operatorNode.raw);
|
|
3086
|
+
if (!quote) {
|
|
3087
|
+
continue;
|
|
3088
|
+
}
|
|
3089
|
+
yield fixer.replaceText(operatorNode, quote + operatorReplacement + quote);
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
};
|
|
3093
|
+
}
|
|
3094
|
+
function reportNode(source, specifiers) {
|
|
3095
|
+
const replacement = getSourceReplacement(source.raw);
|
|
3096
|
+
if (!replacement || hasDeprecatedOperators(specifiers)) {
|
|
3097
|
+
context.report({
|
|
3098
|
+
messageId: "forbiddenWithoutFix",
|
|
3099
|
+
node: source
|
|
3100
|
+
});
|
|
3101
|
+
return;
|
|
3102
|
+
}
|
|
3103
|
+
if (!specifiers) {
|
|
3104
|
+
context.report({
|
|
3105
|
+
messageId: "forbiddenWithoutFix",
|
|
3106
|
+
node: source,
|
|
3107
|
+
suggest: [{ messageId: "suggest", fix: (fixer) => fixer.replaceText(source, replacement) }]
|
|
3108
|
+
});
|
|
3109
|
+
return;
|
|
3110
|
+
}
|
|
3111
|
+
const fix = createFix(source, replacement, specifiers);
|
|
3112
|
+
context.report({
|
|
3113
|
+
fix,
|
|
3114
|
+
messageId: "forbidden",
|
|
3115
|
+
node: source,
|
|
3116
|
+
suggest: [{ messageId: "suggest", fix }]
|
|
3117
|
+
});
|
|
3118
|
+
}
|
|
3119
|
+
return {
|
|
3120
|
+
'ImportDeclaration[source.value="rxjs/operators"]': (node) => {
|
|
3121
|
+
const specifiers = node.specifiers.length && node.specifiers.every((importClause) => isImportSpecifier(importClause)) ? node.specifiers : void 0;
|
|
3122
|
+
reportNode(node.source, specifiers);
|
|
3123
|
+
},
|
|
3124
|
+
'ImportExpression[source.value="rxjs/operators"]': (node) => {
|
|
3125
|
+
if (isLiteral(node.source)) {
|
|
3126
|
+
reportNode(node.source);
|
|
3127
|
+
}
|
|
3128
|
+
},
|
|
3129
|
+
'ExportNamedDeclaration[source.value="rxjs/operators"]': (node) => {
|
|
3130
|
+
reportNode(node.source, node.specifiers);
|
|
3131
|
+
},
|
|
3132
|
+
'ExportAllDeclaration[source.value="rxjs/operators"]': (node) => {
|
|
3133
|
+
reportNode(node.source);
|
|
3134
|
+
}
|
|
3135
|
+
};
|
|
3136
|
+
}
|
|
3137
|
+
});
|
|
3138
|
+
|
|
2897
3139
|
// src/rules/suffix-subjects.ts
|
|
2898
|
-
import { AST_NODE_TYPES as AST_NODE_TYPES7, ESLintUtils as
|
|
3140
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES7, ESLintUtils as ESLintUtils10 } from "@typescript-eslint/utils";
|
|
2899
3141
|
var defaultOptions13 = [];
|
|
2900
3142
|
var suffixSubjectsRule = ruleCreator({
|
|
2901
3143
|
defaultOptions: defaultOptions13,
|
|
@@ -2923,7 +3165,7 @@ var suffixSubjectsRule = ruleCreator({
|
|
|
2923
3165
|
},
|
|
2924
3166
|
name: "suffix-subjects",
|
|
2925
3167
|
create: (context) => {
|
|
2926
|
-
const { esTreeNodeToTSNodeMap } =
|
|
3168
|
+
const { esTreeNodeToTSNodeMap } = ESLintUtils10.getParserServices(context);
|
|
2927
3169
|
const { couldBeType: couldBeType2 } = getTypeServices(context);
|
|
2928
3170
|
const [config = {}] = context.options;
|
|
2929
3171
|
const validate = {
|
|
@@ -3093,7 +3335,7 @@ var suffixSubjectsRule = ruleCreator({
|
|
|
3093
3335
|
});
|
|
3094
3336
|
|
|
3095
3337
|
// src/rules/throw-error.ts
|
|
3096
|
-
import { ESLintUtils as
|
|
3338
|
+
import { ESLintUtils as ESLintUtils11 } from "@typescript-eslint/utils";
|
|
3097
3339
|
import * as tsutils3 from "ts-api-utils";
|
|
3098
3340
|
var defaultOptions14 = [];
|
|
3099
3341
|
var throwErrorRule = ruleCreator({
|
|
@@ -3119,7 +3361,7 @@ var throwErrorRule = ruleCreator({
|
|
|
3119
3361
|
},
|
|
3120
3362
|
name: "throw-error",
|
|
3121
3363
|
create: (context) => {
|
|
3122
|
-
const { esTreeNodeToTSNodeMap, program, getTypeAtLocation } =
|
|
3364
|
+
const { esTreeNodeToTSNodeMap, program, getTypeAtLocation } = ESLintUtils11.getParserServices(context);
|
|
3123
3365
|
const { couldBeObservable } = getTypeServices(context);
|
|
3124
3366
|
const [config = {}] = context.options;
|
|
3125
3367
|
const { allowThrowingAny = true, allowThrowingUnknown = true } = config;
|
|
@@ -3211,6 +3453,7 @@ var plugin = {
|
|
|
3211
3453
|
"no-unsafe-switchmap": noUnsafeSwitchmapRule,
|
|
3212
3454
|
"no-unsafe-takeuntil": noUnsafeTakeuntilRule,
|
|
3213
3455
|
"prefer-observer": preferObserverRule,
|
|
3456
|
+
"prefer-root-operators": preferRootOperatorsRule,
|
|
3214
3457
|
"suffix-subjects": suffixSubjectsRule,
|
|
3215
3458
|
"throw-error": throwErrorRule
|
|
3216
3459
|
}
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
<!-- end auto-generated rule header -->
|
|
8
8
|
|
|
9
9
|
This rule effects failures if async functions are passed to `subscribe`.
|
|
10
|
+
Developers are encouraged to avoid race conditions
|
|
11
|
+
by instead using RxJS operators which can handle both Promises and Observables
|
|
12
|
+
(e.g. `concatMap`, `switchMap`, `mergeMap`, `exhaustMap`).
|
|
10
13
|
|
|
11
14
|
## Rule details
|
|
12
15
|
|
|
@@ -14,16 +17,26 @@ Examples of **incorrect** code for this rule:
|
|
|
14
17
|
|
|
15
18
|
```ts
|
|
16
19
|
import { of } from "rxjs";
|
|
17
|
-
|
|
20
|
+
|
|
21
|
+
of(42).subscribe(async value => {
|
|
22
|
+
const data1 = await fetch(`https://api.some.com/things/${value}`);
|
|
23
|
+
const data2 = await fetch(`https://api.some.com/things/${data1.id}`);
|
|
24
|
+
console.log(data2);
|
|
25
|
+
});
|
|
18
26
|
```
|
|
19
27
|
|
|
20
28
|
Examples of **correct** code for this rule:
|
|
21
29
|
|
|
22
30
|
```ts
|
|
23
31
|
import { of } from "rxjs";
|
|
24
|
-
|
|
32
|
+
|
|
33
|
+
of(42).pipe(
|
|
34
|
+
switchMap(value => fetch(`http://api.some.com/things/${value}`)),
|
|
35
|
+
switchMap(data1 => fetch(`http://api.some.com/things/${data1.id}`)),
|
|
36
|
+
).subscribe(data2 => console.log(data2));
|
|
25
37
|
```
|
|
26
38
|
|
|
27
39
|
## Further reading
|
|
28
40
|
|
|
29
41
|
- [Why does this rule exist?](https://stackoverflow.com/q/71559135)
|
|
42
|
+
- [Higher-order Observables](https://rxjs.dev/guide/higher-order-observables)
|
package/docs/rules/no-compat.md
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# Disallow the `rxjs-compat` package (`rxjs-x/no-compat`)
|
|
2
2
|
|
|
3
|
+
❌ This rule is deprecated.
|
|
4
|
+
|
|
3
5
|
<!-- end auto-generated rule header -->
|
|
4
6
|
|
|
5
7
|
This rule prevents importing from locations that depend upon the `rxjs-compat` package.
|
|
6
8
|
|
|
9
|
+
This rule is deprecated because RxJS v7+ no longer includes `rxjs-compat`.
|
|
10
|
+
|
|
7
11
|
## Further reading
|
|
8
12
|
|
|
9
13
|
- [Backwards compatibility](https://github.com/ReactiveX/rxjs/blob/a6590e971969c736a15b77154dabbc22275aa0d5/docs_app/content/guide/v6/migration.md#backwards-compatibility)
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
# Disallow use of the `toPromise` method (`rxjs-x/no-topromise`)
|
|
2
2
|
|
|
3
|
+
💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).
|
|
4
|
+
|
|
3
5
|
💭 This rule requires [type information](https://typescript-eslint.io/linting/typed-linting).
|
|
4
6
|
|
|
5
7
|
<!-- end auto-generated rule header -->
|
|
6
8
|
|
|
7
9
|
This rule effects failures if the `toPromise` method is used.
|
|
10
|
+
|
|
11
|
+
## Further reading
|
|
12
|
+
|
|
13
|
+
- [Conversion to Promises](https://rxjs.dev/deprecations/to-promise)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Disallow importing operators from `rxjs/operators` (`rxjs-x/prefer-root-operators`)
|
|
2
|
+
|
|
3
|
+
🔧💡 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix) and manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
This rule prevents importing from the `rxjs/operators` export site.
|
|
8
|
+
Most operators were moved to the `rxjs` export site in RxJS v7.2.0
|
|
9
|
+
(excepting a few old and deprecated operators).
|
|
10
|
+
The `rxjs/operators` export site has since been deprecated and will be removed in a future major version.
|
|
11
|
+
|
|
12
|
+
Note that because a few operators were renamed or not migrated to the `rxjs` export site,
|
|
13
|
+
this rule may not provide an automatic fixer if renaming the import path is not guaranteed to be safe.
|
|
14
|
+
See the documentation linked below.
|
|
15
|
+
|
|
16
|
+
## Rule details
|
|
17
|
+
|
|
18
|
+
Examples of **incorrect** code for this rule:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { map } from 'rxjs/operators';
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Examples of **correct** code for this rule:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { map } from 'rxjs';
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Further reading
|
|
31
|
+
|
|
32
|
+
- [Importing instructions](https://rxjs.dev/guide/importing)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-rxjs-x",
|
|
3
3
|
"type": "commonjs",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.2",
|
|
5
5
|
"packageManager": "yarn@4.5.1+sha512.341db9396b6e289fecc30cd7ab3af65060e05ebff4b3b47547b278b9e67b08f485ecd8c79006b405446262142c7a38154445ef7f17c1d5d1de7d90bf9ce7054d",
|
|
6
6
|
"description": "ESLint v9+ plugin for RxJS",
|
|
7
7
|
"author": "Jason Weinzierl <weinzierljason@gmail.com>",
|
|
@@ -84,13 +84,12 @@
|
|
|
84
84
|
"eslint-config-flat-gitignore": "^0.3.0",
|
|
85
85
|
"eslint-doc-generator": "^1.7.1",
|
|
86
86
|
"eslint-import-resolver-typescript": "^3.6.3",
|
|
87
|
-
"eslint-plugin-eslint-plugin": "^6.3.
|
|
87
|
+
"eslint-plugin-eslint-plugin": "^6.3.2",
|
|
88
88
|
"eslint-plugin-import-x": "^4.4.2",
|
|
89
|
-
"eslint-plugin-n": "^17.13.
|
|
89
|
+
"eslint-plugin-n": "^17.13.2",
|
|
90
90
|
"markdownlint-cli2": "^0.15.0",
|
|
91
91
|
"rxjs": "^7.8.1",
|
|
92
92
|
"tsup": "^8.3.5",
|
|
93
|
-
"tsx": "^4.19.2",
|
|
94
93
|
"typescript": "~5.6.3",
|
|
95
94
|
"typescript-eslint": "^8.14.0",
|
|
96
95
|
"vitest": "^2.1.5"
|