eslint-plugin-rxjs-x 0.0.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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +89 -0
  3. package/dist/index.cjs +2936 -0
  4. package/dist/index.d.cts +121 -0
  5. package/dist/index.d.mts +121 -0
  6. package/dist/index.d.ts +121 -0
  7. package/dist/index.mjs +2916 -0
  8. package/docs/rules/ban-observables.md +22 -0
  9. package/docs/rules/ban-operators.md +21 -0
  10. package/docs/rules/finnish.md +54 -0
  11. package/docs/rules/just.md +11 -0
  12. package/docs/rules/macro.md +7 -0
  13. package/docs/rules/no-async-subscribe.md +27 -0
  14. package/docs/rules/no-compat.md +11 -0
  15. package/docs/rules/no-connectable.md +7 -0
  16. package/docs/rules/no-create.md +29 -0
  17. package/docs/rules/no-cyclic-action.md +62 -0
  18. package/docs/rules/no-explicit-generics.md +23 -0
  19. package/docs/rules/no-exposed-subjects.md +39 -0
  20. package/docs/rules/no-finnish.md +25 -0
  21. package/docs/rules/no-ignored-error.md +37 -0
  22. package/docs/rules/no-ignored-notifier.md +30 -0
  23. package/docs/rules/no-ignored-observable.md +23 -0
  24. package/docs/rules/no-ignored-replay-buffer.md +28 -0
  25. package/docs/rules/no-ignored-subscribe.md +28 -0
  26. package/docs/rules/no-ignored-subscription.md +33 -0
  27. package/docs/rules/no-ignored-takewhile-value.md +26 -0
  28. package/docs/rules/no-implicit-any-catch.md +78 -0
  29. package/docs/rules/no-index.md +21 -0
  30. package/docs/rules/no-internal.md +21 -0
  31. package/docs/rules/no-nested-subscribe.md +30 -0
  32. package/docs/rules/no-redundant-notify.md +32 -0
  33. package/docs/rules/no-sharereplay.md +22 -0
  34. package/docs/rules/no-subclass.md +7 -0
  35. package/docs/rules/no-subject-unsubscribe.md +11 -0
  36. package/docs/rules/no-subject-value.md +7 -0
  37. package/docs/rules/no-subscribe-handlers.md +39 -0
  38. package/docs/rules/no-tap.md +7 -0
  39. package/docs/rules/no-topromise.md +7 -0
  40. package/docs/rules/no-unbound-methods.md +47 -0
  41. package/docs/rules/no-unsafe-catch.md +50 -0
  42. package/docs/rules/no-unsafe-first.md +16 -0
  43. package/docs/rules/no-unsafe-subject-next.md +30 -0
  44. package/docs/rules/no-unsafe-switchmap.md +38 -0
  45. package/docs/rules/no-unsafe-takeuntil.md +46 -0
  46. package/docs/rules/prefer-observer.md +34 -0
  47. package/docs/rules/suffix-subjects.md +42 -0
  48. package/docs/rules/throw-error.md +49 -0
  49. package/package.json +82 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,2936 @@
1
+ 'use strict';
2
+
3
+ const utils = require('@typescript-eslint/utils');
4
+ const commonTags = require('common-tags');
5
+ const tsutils = require('tsutils-etc');
6
+ const ts = require('typescript');
7
+ const scopeManager = require('@typescript-eslint/scope-manager');
8
+ const tsutils$1 = require('tsutils');
9
+ const decamelize = require('decamelize');
10
+
11
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
12
+
13
+ function _interopNamespaceCompat(e) {
14
+ if (e && typeof e === 'object' && 'default' in e) return e;
15
+ const n = Object.create(null);
16
+ if (e) {
17
+ for (const k in e) {
18
+ n[k] = e[k];
19
+ }
20
+ }
21
+ n.default = e;
22
+ return n;
23
+ }
24
+
25
+ const tsutils__namespace = /*#__PURE__*/_interopNamespaceCompat(tsutils);
26
+ const ts__namespace = /*#__PURE__*/_interopNamespaceCompat(ts);
27
+ const tsutils__namespace$1 = /*#__PURE__*/_interopNamespaceCompat(tsutils$1);
28
+ const decamelize__default = /*#__PURE__*/_interopDefaultCompat(decamelize);
29
+
30
+ const name = "eslint-plugin-rxjs-x";
31
+ const version = "0.0.1";
32
+
33
+ const createRecommendedConfig = (plugin) => ({
34
+ plugins: {
35
+ "rxjs-x": plugin
36
+ },
37
+ rules: {
38
+ "rxjs-x/no-async-subscribe": "error",
39
+ "rxjs-x/no-create": "error",
40
+ "rxjs-x/no-ignored-notifier": "error",
41
+ "rxjs-x/no-ignored-replay-buffer": "error",
42
+ "rxjs-x/no-ignored-takewhile-value": "error",
43
+ "rxjs-x/no-implicit-any-catch": "error",
44
+ "rxjs-x/no-index": "error",
45
+ "rxjs-x/no-internal": "error",
46
+ "rxjs-x/no-nested-subscribe": "error",
47
+ "rxjs-x/no-redundant-notify": "error",
48
+ "rxjs-x/no-sharereplay": ["error", { allowConfig: true }],
49
+ "rxjs-x/no-subject-unsubscribe": "error",
50
+ "rxjs-x/no-unbound-methods": "error",
51
+ "rxjs-x/no-unsafe-subject-next": "error",
52
+ "rxjs-x/no-unsafe-takeuntil": "error"
53
+ }
54
+ });
55
+
56
+ function createRegExpForWords(config) {
57
+ if (!config?.length) {
58
+ return void 0;
59
+ }
60
+ const flags = "i";
61
+ if (typeof config === "string") {
62
+ return new RegExp(config, flags);
63
+ }
64
+ const words = config;
65
+ const joined = words.map((word) => String.raw`(\b|_)${word}(\b|_)`).join("|");
66
+ return new RegExp(`(${joined})`, flags);
67
+ }
68
+ function escapeRegExp(text) {
69
+ return text.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
70
+ }
71
+ const ruleCreator = utils.ESLintUtils.RuleCreator(
72
+ (name) => `https://github.com/JasonWeinzierl/eslint-plugin-rxjs-x/blob/main/docs/rules/${name}.md`
73
+ );
74
+
75
+ const defaultOptions$c = [];
76
+ const banObservablesRule = ruleCreator({
77
+ defaultOptions: defaultOptions$c,
78
+ meta: {
79
+ docs: {
80
+ description: "Forbids the use of banned observables."
81
+ },
82
+ messages: {
83
+ forbidden: "RxJS observable is banned: {{name}}{{explanation}}."
84
+ },
85
+ schema: [
86
+ {
87
+ type: "object",
88
+ description: commonTags.stripIndent`
89
+ An object containing keys that are names of observable factory functions
90
+ and values that are either booleans or strings containing the explanation for the ban.`
91
+ }
92
+ ],
93
+ type: "problem"
94
+ },
95
+ name: "ban-observables",
96
+ create: (context) => {
97
+ const bans = [];
98
+ const [config] = context.options;
99
+ if (!config) {
100
+ return {};
101
+ }
102
+ Object.entries(config).forEach(([key, value]) => {
103
+ if (value !== false) {
104
+ bans.push({
105
+ explanation: typeof value === "string" ? value : "",
106
+ regExp: new RegExp(`^${key}$`)
107
+ });
108
+ }
109
+ });
110
+ function getFailure(name) {
111
+ for (let b = 0, length = bans.length; b < length; ++b) {
112
+ const ban = bans[b];
113
+ if (ban.regExp.test(name)) {
114
+ const explanation = ban.explanation ? `: ${ban.explanation}` : "";
115
+ return {
116
+ messageId: "forbidden",
117
+ data: { name, explanation }
118
+ };
119
+ }
120
+ }
121
+ return void 0;
122
+ }
123
+ return {
124
+ "ImportDeclaration[source.value='rxjs'] > ImportSpecifier": (node) => {
125
+ const identifier = node.imported;
126
+ const name = identifier.type === utils.AST_NODE_TYPES.Identifier ? identifier.name : identifier.value;
127
+ const failure = getFailure(name);
128
+ if (failure) {
129
+ context.report({
130
+ ...failure,
131
+ node: identifier
132
+ });
133
+ }
134
+ }
135
+ };
136
+ }
137
+ });
138
+
139
+ const defaultOptions$b = [];
140
+ const banOperatorsRule = ruleCreator({
141
+ defaultOptions: defaultOptions$b,
142
+ meta: {
143
+ docs: {
144
+ description: "Forbids the use of banned operators."
145
+ },
146
+ messages: {
147
+ forbidden: "RxJS operator is banned: {{name}}{{explanation}}."
148
+ },
149
+ schema: [
150
+ {
151
+ type: "object",
152
+ description: commonTags.stripIndent`
153
+ An object containing keys that are names of operators
154
+ and values that are either booleans or strings containing the explanation for the ban.`
155
+ }
156
+ ],
157
+ type: "problem"
158
+ },
159
+ name: "ban-operators",
160
+ create: (context) => {
161
+ const bans = [];
162
+ const [config] = context.options;
163
+ if (!config) {
164
+ return {};
165
+ }
166
+ Object.entries(config).forEach(([key, value]) => {
167
+ if (value !== false) {
168
+ bans.push({
169
+ explanation: typeof value === "string" ? value : "",
170
+ regExp: new RegExp(`^${key}$`)
171
+ });
172
+ }
173
+ });
174
+ function getFailure(name) {
175
+ for (let b = 0, length = bans.length; b < length; ++b) {
176
+ const ban = bans[b];
177
+ if (ban.regExp.test(name)) {
178
+ const explanation = ban.explanation ? `: ${ban.explanation}` : "";
179
+ return {
180
+ messageId: "forbidden",
181
+ data: { name, explanation }
182
+ };
183
+ }
184
+ }
185
+ return void 0;
186
+ }
187
+ return {
188
+ [String.raw`ImportDeclaration[source.value=/^rxjs\u002foperators$/] > ImportSpecifier`]: (node) => {
189
+ const identifier = node.imported;
190
+ const name = identifier.type === utils.AST_NODE_TYPES.Identifier ? identifier.name : identifier.value;
191
+ const failure = getFailure(name);
192
+ if (failure) {
193
+ context.report({
194
+ ...failure,
195
+ node: identifier
196
+ });
197
+ }
198
+ }
199
+ };
200
+ }
201
+ });
202
+
203
+ function findParent(node, ...args) {
204
+ const [arg] = args;
205
+ const predicate = typeof arg === "function" ? arg : (type) => !args.includes(type) ? "continue" : "return";
206
+ let parent = node.parent;
207
+ while (parent) {
208
+ switch (predicate(parent.type)) {
209
+ case "break":
210
+ return void 0;
211
+ case "return":
212
+ return parent;
213
+ }
214
+ parent = parent.parent;
215
+ }
216
+ return void 0;
217
+ }
218
+
219
+ function getLoc(node) {
220
+ const sourceFile = node.getSourceFile();
221
+ const start = ts__namespace.getLineAndCharacterOfPosition(sourceFile, node.getStart());
222
+ const end = ts__namespace.getLineAndCharacterOfPosition(sourceFile, node.getEnd());
223
+ return {
224
+ start: {
225
+ line: start.line + 1,
226
+ column: start.character
227
+ },
228
+ end: {
229
+ line: end.line + 1,
230
+ column: end.character
231
+ }
232
+ };
233
+ }
234
+
235
+ function hasTypeAnnotation(node) {
236
+ return "typeAnnotation" in node && !!node.typeAnnotation;
237
+ }
238
+ function isArrayExpression(node) {
239
+ return node.type === utils.AST_NODE_TYPES.ArrayExpression;
240
+ }
241
+ function isArrayPattern(node) {
242
+ return node.type === utils.AST_NODE_TYPES.ArrayPattern;
243
+ }
244
+ function isArrowFunctionExpression(node) {
245
+ return node.type === utils.AST_NODE_TYPES.ArrowFunctionExpression;
246
+ }
247
+ function isBlockStatement(node) {
248
+ return node.type === utils.AST_NODE_TYPES.BlockStatement;
249
+ }
250
+ function isCallExpression(node) {
251
+ return node.type === utils.AST_NODE_TYPES.CallExpression;
252
+ }
253
+ function isFunctionDeclaration(node) {
254
+ return node.type === utils.AST_NODE_TYPES.FunctionDeclaration;
255
+ }
256
+ function isFunctionExpression(node) {
257
+ return node.type === utils.AST_NODE_TYPES.FunctionExpression;
258
+ }
259
+ function isIdentifier(node) {
260
+ return node.type === utils.AST_NODE_TYPES.Identifier;
261
+ }
262
+ function isLiteral(node) {
263
+ return node.type === utils.AST_NODE_TYPES.Literal;
264
+ }
265
+ function isMemberExpression(node) {
266
+ return node.type === utils.AST_NODE_TYPES.MemberExpression;
267
+ }
268
+ function isObjectExpression(node) {
269
+ return node.type === utils.AST_NODE_TYPES.ObjectExpression;
270
+ }
271
+ function isObjectPattern(node) {
272
+ return node.type === utils.AST_NODE_TYPES.ObjectPattern;
273
+ }
274
+ function isProgram(node) {
275
+ return node.type === utils.AST_NODE_TYPES.Program;
276
+ }
277
+ function isProperty(node) {
278
+ return node.type === utils.AST_NODE_TYPES.Property;
279
+ }
280
+
281
+ function getTypeServices(context) {
282
+ const services = utils.ESLintUtils.getParserServices(context);
283
+ const { esTreeNodeToTSNodeMap, program, getTypeAtLocation } = services;
284
+ const typeChecker = program.getTypeChecker();
285
+ const couldBeType = (node, name, qualified) => {
286
+ const type = getType(node);
287
+ return tsutils__namespace.couldBeType(
288
+ type,
289
+ name,
290
+ qualified ? { ...qualified, typeChecker } : void 0
291
+ );
292
+ };
293
+ const couldReturnType = (node, name, qualified) => {
294
+ let tsTypeNode;
295
+ const tsNode = esTreeNodeToTSNodeMap.get(node);
296
+ if (ts__namespace.isArrowFunction(tsNode) || ts__namespace.isFunctionDeclaration(tsNode) || ts__namespace.isMethodDeclaration(tsNode) || ts__namespace.isFunctionExpression(tsNode)) {
297
+ tsTypeNode = tsNode.type ?? tsNode.body;
298
+ } else if (ts__namespace.isCallSignatureDeclaration(tsNode) || ts__namespace.isMethodSignature(tsNode)) {
299
+ tsTypeNode = tsNode.type;
300
+ }
301
+ return Boolean(
302
+ tsTypeNode && tsutils__namespace.couldBeType(
303
+ typeChecker.getTypeAtLocation(tsTypeNode),
304
+ name,
305
+ qualified ? { ...qualified, typeChecker } : void 0
306
+ )
307
+ );
308
+ };
309
+ const getType = (node) => {
310
+ return getTypeAtLocation(node);
311
+ };
312
+ return {
313
+ couldBeBehaviorSubject: (node) => couldBeType(node, "BehaviorSubject"),
314
+ couldBeError: (node) => couldBeType(node, "Error"),
315
+ couldBeFunction: (node) => {
316
+ if (isArrowFunctionExpression(node) || isFunctionDeclaration(node)) {
317
+ return true;
318
+ }
319
+ return tsutils__namespace.couldBeFunction(getType(node));
320
+ },
321
+ couldBeMonoTypeOperatorFunction: (node) => couldBeType(node, "MonoTypeOperatorFunction"),
322
+ couldBeObservable: (node) => couldBeType(node, "Observable"),
323
+ couldBeSubject: (node) => couldBeType(node, "Subject"),
324
+ couldBeSubscription: (node) => couldBeType(node, "Subscription"),
325
+ couldBeType,
326
+ couldReturnObservable: (node) => couldReturnType(node, "Observable"),
327
+ couldReturnType,
328
+ getType,
329
+ isAny: (node) => tsutils__namespace.isAny(getType(node)),
330
+ isReferenceType: (node) => tsutils__namespace.isReferenceType(getType(node)),
331
+ isUnknown: (node) => tsutils__namespace.isUnknown(getType(node)),
332
+ typeChecker
333
+ };
334
+ }
335
+
336
+ function isImport(scope, name, source) {
337
+ const variable = scope.variables.find((variable2) => variable2.name === name);
338
+ if (variable) {
339
+ return variable.defs.some(
340
+ (def) => def.type === scopeManager.DefinitionType.ImportBinding && def.parent.type === utils.AST_NODE_TYPES.ImportDeclaration && (typeof source === "string" ? def.parent.source.value === source : source.test(def.parent.source.value))
341
+ );
342
+ }
343
+ return scope.upper ? isImport(scope.upper, name, source) : false;
344
+ }
345
+
346
+ const defaultOptions$a = [];
347
+ const finnishRule = ruleCreator({
348
+ defaultOptions: defaultOptions$a,
349
+ meta: {
350
+ docs: {
351
+ description: "Enforces the use of Finnish notation.",
352
+ requiresTypeChecking: true
353
+ },
354
+ messages: {
355
+ shouldBeFinnish: "Finnish notation should be used here.",
356
+ shouldNotBeFinnish: "Finnish notation should not be used here."
357
+ },
358
+ schema: [
359
+ {
360
+ properties: {
361
+ functions: { type: "boolean" },
362
+ methods: { type: "boolean" },
363
+ names: { type: "object" },
364
+ parameters: { type: "boolean" },
365
+ properties: { type: "boolean" },
366
+ strict: { type: "boolean" },
367
+ types: { type: "object" },
368
+ variables: { type: "boolean" }
369
+ },
370
+ type: "object"
371
+ }
372
+ ],
373
+ type: "problem"
374
+ },
375
+ name: "finnish",
376
+ create: (context) => {
377
+ const { esTreeNodeToTSNodeMap } = utils.ESLintUtils.getParserServices(context);
378
+ const {
379
+ couldBeObservable,
380
+ couldBeType,
381
+ couldReturnObservable,
382
+ couldReturnType
383
+ } = getTypeServices(context);
384
+ const [config = {}] = context.options;
385
+ const { strict = false } = config;
386
+ const validate = {
387
+ functions: true,
388
+ methods: true,
389
+ parameters: true,
390
+ properties: true,
391
+ variables: true,
392
+ ...config
393
+ };
394
+ const names = [];
395
+ if (config.names) {
396
+ Object.entries(config.names).forEach(
397
+ ([key, validate2]) => {
398
+ names.push({ regExp: new RegExp(key), validate: validate2 });
399
+ }
400
+ );
401
+ } else {
402
+ names.push({
403
+ regExp: /^(canActivate|canActivateChild|canDeactivate|canLoad|intercept|resolve|validate)$/,
404
+ validate: false
405
+ });
406
+ }
407
+ const types = [];
408
+ if (config.types) {
409
+ Object.entries(config.types).forEach(
410
+ ([key, validate2]) => {
411
+ types.push({ regExp: new RegExp(key), validate: validate2 });
412
+ }
413
+ );
414
+ } else {
415
+ types.push({
416
+ regExp: /^EventEmitter$/,
417
+ validate: false
418
+ });
419
+ }
420
+ function checkNode(nameNode, typeNode) {
421
+ const tsNode = esTreeNodeToTSNodeMap.get(nameNode);
422
+ const text = tsNode.getText();
423
+ const hasFinnish = text.endsWith("$");
424
+ if (hasFinnish && !strict) {
425
+ return;
426
+ }
427
+ const shouldBeFinnish = hasFinnish ? () => {
428
+ } : () => {
429
+ context.report({
430
+ loc: getLoc(tsNode),
431
+ messageId: "shouldBeFinnish"
432
+ });
433
+ };
434
+ const shouldNotBeFinnish = hasFinnish ? () => {
435
+ context.report({
436
+ loc: getLoc(tsNode),
437
+ messageId: "shouldNotBeFinnish"
438
+ });
439
+ } : () => {
440
+ };
441
+ if (couldBeObservable(typeNode || nameNode) || couldReturnObservable(typeNode || nameNode)) {
442
+ for (const name of names) {
443
+ const { regExp, validate: validate2 } = name;
444
+ if (regExp.test(text) && !validate2) {
445
+ shouldNotBeFinnish();
446
+ return;
447
+ }
448
+ }
449
+ for (const type of types) {
450
+ const { regExp, validate: validate2 } = type;
451
+ if ((couldBeType(typeNode || nameNode, regExp) || couldReturnType(typeNode || nameNode, regExp)) && !validate2) {
452
+ shouldNotBeFinnish();
453
+ return;
454
+ }
455
+ }
456
+ shouldBeFinnish();
457
+ } else {
458
+ shouldNotBeFinnish();
459
+ }
460
+ }
461
+ return {
462
+ "ArrayPattern > Identifier": (node) => {
463
+ const found = findParent(
464
+ node,
465
+ "ArrowFunctionExpression",
466
+ "FunctionDeclaration",
467
+ "FunctionExpression",
468
+ "VariableDeclarator"
469
+ );
470
+ if (!found) {
471
+ return;
472
+ }
473
+ if (!validate.variables && found.type === utils.AST_NODE_TYPES.VariableDeclarator) {
474
+ return;
475
+ }
476
+ if (!validate.parameters) {
477
+ return;
478
+ }
479
+ checkNode(node);
480
+ },
481
+ "ArrowFunctionExpression > Identifier": (node) => {
482
+ if (validate.parameters) {
483
+ const parent = node.parent;
484
+ if (node !== parent.body) {
485
+ checkNode(node);
486
+ }
487
+ }
488
+ },
489
+ "PropertyDefinition[computed=false]": (node) => {
490
+ if (validate.properties) {
491
+ checkNode(node.key);
492
+ }
493
+ },
494
+ "FunctionDeclaration > Identifier": (node) => {
495
+ const parent = node.parent;
496
+ if (node === parent.id) {
497
+ if (validate.functions) {
498
+ checkNode(node, parent);
499
+ }
500
+ } else {
501
+ if (validate.parameters) {
502
+ checkNode(node);
503
+ }
504
+ }
505
+ },
506
+ "FunctionExpression > Identifier": (node) => {
507
+ const parent = node.parent;
508
+ if (node === parent.id) {
509
+ if (validate.functions) {
510
+ checkNode(node, parent);
511
+ }
512
+ } else {
513
+ if (validate.parameters) {
514
+ checkNode(node);
515
+ }
516
+ }
517
+ },
518
+ "MethodDefinition[kind='get'][computed=false]": (node) => {
519
+ if (validate.properties) {
520
+ checkNode(node.key, node);
521
+ }
522
+ },
523
+ "MethodDefinition[kind='method'][computed=false]": (node) => {
524
+ if (validate.methods) {
525
+ checkNode(node.key, node);
526
+ }
527
+ },
528
+ "MethodDefinition[kind='set'][computed=false]": (node) => {
529
+ if (validate.properties) {
530
+ checkNode(node.key, node);
531
+ }
532
+ },
533
+ "ObjectExpression > Property[computed=false] > Identifier": (node) => {
534
+ if (validate.properties) {
535
+ const parent = node.parent;
536
+ if (node === parent.key) {
537
+ checkNode(node);
538
+ }
539
+ }
540
+ },
541
+ "ObjectPattern > Property > Identifier": (node) => {
542
+ const found = findParent(
543
+ node,
544
+ "ArrowFunctionExpression",
545
+ "FunctionDeclaration",
546
+ "FunctionExpression",
547
+ "VariableDeclarator"
548
+ );
549
+ if (!found) {
550
+ return;
551
+ }
552
+ if (!validate.variables && found.type === utils.AST_NODE_TYPES.VariableDeclarator) {
553
+ return;
554
+ }
555
+ if (!validate.parameters) {
556
+ return;
557
+ }
558
+ const parent = node.parent;
559
+ if (node === parent.value) {
560
+ checkNode(node);
561
+ }
562
+ },
563
+ "TSCallSignatureDeclaration > Identifier": (node) => {
564
+ if (validate.parameters) {
565
+ checkNode(node);
566
+ }
567
+ },
568
+ "TSConstructSignatureDeclaration > Identifier": (node) => {
569
+ if (validate.parameters) {
570
+ checkNode(node);
571
+ }
572
+ },
573
+ "TSMethodSignature[computed=false]": (node) => {
574
+ if (validate.methods) {
575
+ checkNode(node.key, node);
576
+ }
577
+ if (validate.parameters) {
578
+ node.params.forEach((param) => {
579
+ checkNode(param);
580
+ });
581
+ }
582
+ },
583
+ "TSParameterProperty > Identifier": (node) => {
584
+ if (validate.parameters || validate.properties) {
585
+ checkNode(node);
586
+ }
587
+ },
588
+ "TSPropertySignature[computed=false]": (node) => {
589
+ if (validate.properties) {
590
+ checkNode(node.key);
591
+ }
592
+ },
593
+ "VariableDeclarator > Identifier": (node) => {
594
+ const parent = node.parent;
595
+ if (validate.variables && node === parent.id) {
596
+ checkNode(node);
597
+ }
598
+ }
599
+ };
600
+ }
601
+ });
602
+
603
+ const justRule = ruleCreator({
604
+ defaultOptions: [],
605
+ meta: {
606
+ docs: {
607
+ description: "Enforces the use of a `just` alias for `of`."
608
+ },
609
+ fixable: "code",
610
+ messages: {
611
+ forbidden: "Use just alias."
612
+ },
613
+ schema: [],
614
+ type: "problem"
615
+ },
616
+ name: "just",
617
+ create: (context) => {
618
+ return {
619
+ "ImportDeclaration[source.value='rxjs'] > ImportSpecifier[imported.name='of']": (node) => {
620
+ if (node.local.range[0] !== node.imported.range[0] && node.local.range[1] !== node.imported.range[1]) {
621
+ return;
622
+ }
623
+ context.report({
624
+ messageId: "forbidden",
625
+ node,
626
+ fix: (fixer) => fixer.replaceTextRange(node.range, "of as just")
627
+ });
628
+ const [ofImport] = context.sourceCode.getDeclaredVariables(node);
629
+ ofImport.references.forEach((ref) => {
630
+ context.report({
631
+ messageId: "forbidden",
632
+ node: ref.identifier,
633
+ fix: (fixer) => fixer.replaceTextRange(ref.identifier.range, "just")
634
+ });
635
+ });
636
+ }
637
+ };
638
+ }
639
+ });
640
+
641
+ const macroRule = ruleCreator({
642
+ defaultOptions: [],
643
+ meta: {
644
+ docs: {
645
+ description: "Enforces the use of the RxJS Tools Babel macro."
646
+ },
647
+ fixable: "code",
648
+ messages: {
649
+ macro: "Use the RxJS Tools Babel macro."
650
+ },
651
+ schema: [],
652
+ type: "problem"
653
+ },
654
+ name: "macro",
655
+ create: (context) => {
656
+ let hasFailure = false;
657
+ let hasMacroImport = false;
658
+ let program = void 0;
659
+ function fix(fixer) {
660
+ return fixer.insertTextBefore(
661
+ /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
662
+ program,
663
+ `import "babel-plugin-rxjs-tools/macro";
664
+ `
665
+ );
666
+ }
667
+ return {
668
+ "CallExpression[callee.property.name=/^(pipe|subscribe)$/]": (node) => {
669
+ if (hasFailure || hasMacroImport) {
670
+ return;
671
+ }
672
+ hasFailure = true;
673
+ context.report({
674
+ fix,
675
+ messageId: "macro",
676
+ node: node.callee
677
+ });
678
+ },
679
+ "ImportDeclaration[source.value='babel-plugin-rxjs-tools/macro']": (_node) => {
680
+ hasMacroImport = true;
681
+ },
682
+ [String.raw`ImportDeclaration[source.value=/^rxjs(\u002f|$)/]`]: (node) => {
683
+ if (hasFailure || hasMacroImport) {
684
+ return;
685
+ }
686
+ hasFailure = true;
687
+ context.report({
688
+ fix,
689
+ messageId: "macro",
690
+ node
691
+ });
692
+ },
693
+ "Program": (node) => {
694
+ program = node;
695
+ }
696
+ };
697
+ }
698
+ });
699
+
700
+ const noAsyncSubscribeRule = ruleCreator({
701
+ defaultOptions: [],
702
+ meta: {
703
+ docs: {
704
+ description: "Forbids passing `async` functions to `subscribe`.",
705
+ recommended: true,
706
+ requiresTypeChecking: true
707
+ },
708
+ messages: {
709
+ forbidden: "Passing async functions to subscribe is forbidden."
710
+ },
711
+ schema: [],
712
+ type: "problem"
713
+ },
714
+ name: "no-async-subscribe",
715
+ create: (context) => {
716
+ const { couldBeObservable } = getTypeServices(context);
717
+ function checkNode(node) {
718
+ const parentNode = node.parent;
719
+ const callee = parentNode.callee;
720
+ if (couldBeObservable(callee.object)) {
721
+ const { loc } = node;
722
+ const asyncLoc = {
723
+ ...loc,
724
+ end: {
725
+ ...loc.start,
726
+ column: loc.start.column + 5
727
+ }
728
+ };
729
+ context.report({
730
+ messageId: "forbidden",
731
+ loc: asyncLoc
732
+ });
733
+ }
734
+ }
735
+ return {
736
+ "CallExpression[callee.property.name='subscribe'] > FunctionExpression[async=true]": checkNode,
737
+ "CallExpression[callee.property.name='subscribe'] > ArrowFunctionExpression[async=true]": checkNode
738
+ };
739
+ }
740
+ });
741
+
742
+ const noCompatRule = ruleCreator({
743
+ defaultOptions: [],
744
+ meta: {
745
+ docs: {
746
+ description: "Forbids importation from locations that depend upon `rxjs-compat`."
747
+ },
748
+ messages: {
749
+ forbidden: "'rxjs-compat'-dependent import locations are forbidden."
750
+ },
751
+ schema: [],
752
+ type: "problem"
753
+ },
754
+ name: "no-compat",
755
+ create: (context) => {
756
+ return {
757
+ [String.raw`ImportDeclaration Literal[value=/^rxjs\u002f/]:not(Literal[value=/^rxjs\u002f(ajax|fetch|operators|testing|webSocket)/])`]: (node) => {
758
+ context.report({
759
+ messageId: "forbidden",
760
+ node
761
+ });
762
+ }
763
+ };
764
+ }
765
+ });
766
+
767
+ const noConnectableRule = ruleCreator({
768
+ defaultOptions: [],
769
+ meta: {
770
+ docs: {
771
+ description: "Forbids operators that return connectable observables.",
772
+ requiresTypeChecking: true
773
+ },
774
+ messages: {
775
+ forbidden: "Connectable observables are forbidden."
776
+ },
777
+ schema: [],
778
+ type: "problem"
779
+ },
780
+ name: "no-connectable",
781
+ create: (context) => {
782
+ const { couldBeFunction } = getTypeServices(context);
783
+ return {
784
+ "CallExpression[callee.name='multicast']": (node) => {
785
+ if (node.arguments.length === 1) {
786
+ context.report({
787
+ messageId: "forbidden",
788
+ node: node.callee
789
+ });
790
+ }
791
+ },
792
+ "CallExpression[callee.name=/^(publish|publishBehavior|publishLast|publishReplay)$/]": (node) => {
793
+ if (!node.arguments.some((arg) => couldBeFunction(arg))) {
794
+ context.report({
795
+ messageId: "forbidden",
796
+ node: node.callee
797
+ });
798
+ }
799
+ }
800
+ };
801
+ }
802
+ });
803
+
804
+ const noCreateRule = ruleCreator({
805
+ defaultOptions: [],
806
+ meta: {
807
+ docs: {
808
+ description: "Forbids the calling of `Observable.create`.",
809
+ recommended: true,
810
+ requiresTypeChecking: true
811
+ },
812
+ messages: {
813
+ forbidden: "Observable.create is forbidden; use new Observable."
814
+ },
815
+ schema: [],
816
+ type: "problem"
817
+ },
818
+ name: "no-create",
819
+ create: (context) => {
820
+ const { couldBeObservable } = getTypeServices(context);
821
+ return {
822
+ "CallExpression > MemberExpression[object.name='Observable'] > Identifier[name='create']": (node) => {
823
+ const memberExpression = node.parent;
824
+ if (couldBeObservable(memberExpression.object)) {
825
+ context.report({
826
+ messageId: "forbidden",
827
+ node
828
+ });
829
+ }
830
+ }
831
+ };
832
+ }
833
+ });
834
+
835
+ const defaultObservable = String.raw`[Aa]ction(s|s\$|\$)$`;
836
+
837
+ function isTypeReference(type) {
838
+ return Boolean(type.target);
839
+ }
840
+ const defaultOptions$9 = [];
841
+ const noCyclicActionRule = ruleCreator({
842
+ defaultOptions: defaultOptions$9,
843
+ meta: {
844
+ docs: {
845
+ description: "Forbids effects and epics that re-emit filtered actions.",
846
+ requiresTypeChecking: true
847
+ },
848
+ messages: {
849
+ forbidden: "Effects and epics that re-emit filtered actions are forbidden."
850
+ },
851
+ schema: [
852
+ {
853
+ properties: {
854
+ observable: { type: "string" }
855
+ },
856
+ type: "object",
857
+ description: commonTags.stripIndent`
858
+ An optional object with an optional \`observable\` property.
859
+ The property can be specified as a regular expression string and is used to identify the action observables from which effects and epics are composed.`
860
+ }
861
+ ],
862
+ type: "problem"
863
+ },
864
+ name: "no-cyclic-action",
865
+ create: (context) => {
866
+ const [config = {}] = context.options;
867
+ const { observable = defaultObservable } = config;
868
+ const observableRegExp = new RegExp(observable);
869
+ const { getType, typeChecker } = getTypeServices(context);
870
+ function checkNode(pipeCallExpression) {
871
+ const operatorCallExpression = pipeCallExpression.arguments.find(
872
+ (arg) => isCallExpression(arg) && isIdentifier(arg.callee) && arg.callee.name === "ofType"
873
+ );
874
+ if (!operatorCallExpression) {
875
+ return;
876
+ }
877
+ const operatorType = getType(operatorCallExpression);
878
+ const [signature] = typeChecker.getSignaturesOfType(
879
+ operatorType,
880
+ ts__namespace.SignatureKind.Call
881
+ );
882
+ if (!signature) {
883
+ return;
884
+ }
885
+ const operatorReturnType = typeChecker.getReturnTypeOfSignature(signature);
886
+ if (!isTypeReference(operatorReturnType)) {
887
+ return;
888
+ }
889
+ const [operatorElementType] = typeChecker.getTypeArguments(operatorReturnType);
890
+ if (!operatorElementType) {
891
+ return;
892
+ }
893
+ const pipeType = getType(pipeCallExpression);
894
+ if (!isTypeReference(pipeType)) {
895
+ return;
896
+ }
897
+ const [pipeElementType] = typeChecker.getTypeArguments(pipeType);
898
+ if (!pipeElementType) {
899
+ return;
900
+ }
901
+ const operatorActionTypes = getActionTypes(operatorElementType);
902
+ const pipeActionTypes = getActionTypes(pipeElementType);
903
+ for (const actionType of operatorActionTypes) {
904
+ if (pipeActionTypes.includes(actionType)) {
905
+ context.report({
906
+ messageId: "forbidden",
907
+ node: pipeCallExpression.callee
908
+ });
909
+ return;
910
+ }
911
+ }
912
+ }
913
+ function getActionTypes(type) {
914
+ if (type.isUnion()) {
915
+ const memberActionTypes = [];
916
+ for (const memberType of type.types) {
917
+ memberActionTypes.push(...getActionTypes(memberType));
918
+ }
919
+ return memberActionTypes;
920
+ }
921
+ const symbol = typeChecker.getPropertyOfType(type, "type");
922
+ if (!symbol?.valueDeclaration) {
923
+ return [];
924
+ }
925
+ const actionType = typeChecker.getTypeOfSymbolAtLocation(
926
+ symbol,
927
+ symbol.valueDeclaration
928
+ );
929
+ return [typeChecker.typeToString(actionType)];
930
+ }
931
+ return {
932
+ [`CallExpression[callee.property.name='pipe'][callee.object.name=${observableRegExp}]`]: checkNode,
933
+ [`CallExpression[callee.property.name='pipe'][callee.object.property.name=${observableRegExp}]`]: checkNode
934
+ };
935
+ }
936
+ });
937
+
938
+ const noExplicitGenericsRule = ruleCreator({
939
+ defaultOptions: [],
940
+ meta: {
941
+ docs: {
942
+ description: "Forbids explicit generic type arguments."
943
+ },
944
+ messages: {
945
+ forbidden: "Explicit generic type arguments are forbidden."
946
+ },
947
+ schema: [],
948
+ type: "problem"
949
+ },
950
+ name: "no-explicit-generics",
951
+ create: (context) => {
952
+ function report(node) {
953
+ context.report({
954
+ messageId: "forbidden",
955
+ node
956
+ });
957
+ }
958
+ function checkBehaviorSubjects(node) {
959
+ const parent = node.parent;
960
+ const {
961
+ arguments: [value]
962
+ } = parent;
963
+ if (isArrayExpression(value) || isObjectExpression(value)) {
964
+ return;
965
+ }
966
+ report(node);
967
+ }
968
+ function checkNotifications(node) {
969
+ const parent = node.parent;
970
+ const {
971
+ arguments: [, value]
972
+ } = parent;
973
+ if (isArrayExpression(value) || isObjectExpression(value)) {
974
+ return;
975
+ }
976
+ report(node);
977
+ }
978
+ return {
979
+ "CallExpression[callee.property.name='pipe'] > CallExpression[typeArguments.params.length > 0] > Identifier": report,
980
+ "NewExpression[typeArguments.params.length > 0] > Identifier[name='BehaviorSubject']": checkBehaviorSubjects,
981
+ "CallExpression[typeArguments.params.length > 0] > Identifier[name=/^(from|of)$/]": report,
982
+ "NewExpression[typeArguments.params.length > 0][arguments.0.value='N'] > Identifier[name='Notification']": checkNotifications
983
+ };
984
+ }
985
+ });
986
+
987
+ const defaultAllowedTypesRegExp = /^EventEmitter$/;
988
+ const defaultOptions$8 = [];
989
+ const noExposedSubjectsRule = ruleCreator({
990
+ defaultOptions: defaultOptions$8,
991
+ meta: {
992
+ docs: {
993
+ description: "Forbids exposed (i.e. non-private) subjects.",
994
+ requiresTypeChecking: true
995
+ },
996
+ messages: {
997
+ forbidden: "Subject '{{subject}}' must be private.",
998
+ forbiddenAllowProtected: "Subject '{{subject}}' must be private or protected."
999
+ },
1000
+ schema: [
1001
+ {
1002
+ properties: {
1003
+ allowProtected: { type: "boolean" }
1004
+ },
1005
+ type: "object"
1006
+ }
1007
+ ],
1008
+ type: "problem"
1009
+ },
1010
+ name: "no-exposed-subjects",
1011
+ create: (context) => {
1012
+ const [config = {}] = context.options;
1013
+ const { allowProtected = false } = config;
1014
+ const { couldBeSubject, couldBeType } = getTypeServices(context);
1015
+ const messageId = allowProtected ? "forbiddenAllowProtected" : "forbidden";
1016
+ const accessibilityRexExp = allowProtected ? /^(private|protected)$/ : /^private$/;
1017
+ function isSubject(node) {
1018
+ return couldBeSubject(node) && !couldBeType(node, defaultAllowedTypesRegExp);
1019
+ }
1020
+ return {
1021
+ [`PropertyDefinition[accessibility!=${accessibilityRexExp}]`]: (node) => {
1022
+ if (isSubject(node)) {
1023
+ const { key } = node;
1024
+ if (isIdentifier(key)) {
1025
+ context.report({
1026
+ messageId,
1027
+ node: key,
1028
+ data: {
1029
+ subject: key.name
1030
+ }
1031
+ });
1032
+ }
1033
+ }
1034
+ },
1035
+ [`MethodDefinition[kind='constructor'] > FunctionExpression > TSParameterProperty[accessibility!=${accessibilityRexExp}] > Identifier`]: (node) => {
1036
+ if (isSubject(node)) {
1037
+ const { loc } = node;
1038
+ context.report({
1039
+ messageId,
1040
+ loc: {
1041
+ ...loc,
1042
+ end: {
1043
+ ...loc.start,
1044
+ column: loc.start.column + node.name.length
1045
+ }
1046
+ },
1047
+ data: {
1048
+ subject: node.name
1049
+ }
1050
+ });
1051
+ }
1052
+ },
1053
+ [`MethodDefinition[accessibility!=${accessibilityRexExp}][kind=/^(get|set)$/]`]: (node) => {
1054
+ if (isSubject(node)) {
1055
+ const key = node.key;
1056
+ context.report({
1057
+ messageId,
1058
+ node: key,
1059
+ data: {
1060
+ subject: key.name
1061
+ }
1062
+ });
1063
+ }
1064
+ },
1065
+ [`MethodDefinition[accessibility!=${accessibilityRexExp}][kind='method']`]: (node) => {
1066
+ const functionExpression = node.value;
1067
+ const returnType = functionExpression.returnType;
1068
+ if (!returnType) {
1069
+ return;
1070
+ }
1071
+ const typeAnnotation = returnType.typeAnnotation;
1072
+ if (!typeAnnotation) {
1073
+ return;
1074
+ }
1075
+ if (isSubject(typeAnnotation)) {
1076
+ const key = node.key;
1077
+ context.report({
1078
+ messageId,
1079
+ node: key,
1080
+ data: {
1081
+ subject: key.name
1082
+ }
1083
+ });
1084
+ }
1085
+ }
1086
+ };
1087
+ }
1088
+ });
1089
+
1090
+ const noFinnishRule = ruleCreator({
1091
+ defaultOptions: [],
1092
+ meta: {
1093
+ docs: {
1094
+ description: "Forbids the use of Finnish notation.",
1095
+ requiresTypeChecking: true
1096
+ },
1097
+ messages: {
1098
+ forbidden: "Finnish notation is forbidden."
1099
+ },
1100
+ schema: [],
1101
+ type: "problem"
1102
+ },
1103
+ name: "no-finnish",
1104
+ create: (context) => {
1105
+ const { esTreeNodeToTSNodeMap } = utils.ESLintUtils.getParserServices(context);
1106
+ const { couldBeObservable, couldReturnObservable } = getTypeServices(context);
1107
+ function checkNode(nameNode, typeNode) {
1108
+ if (couldBeObservable(typeNode || nameNode) || couldReturnObservable(typeNode || nameNode)) {
1109
+ const tsNode = esTreeNodeToTSNodeMap.get(nameNode);
1110
+ if (/[$]+$/.test(tsNode.getText())) {
1111
+ context.report({
1112
+ loc: getLoc(tsNode),
1113
+ messageId: "forbidden"
1114
+ });
1115
+ }
1116
+ }
1117
+ }
1118
+ return {
1119
+ "ArrayPattern > Identifier[name=/[$]+$/]": (node) => {
1120
+ checkNode(node);
1121
+ },
1122
+ "ArrowFunctionExpression > Identifier[name=/[$]+$/]": (node) => {
1123
+ const parent = node.parent;
1124
+ if (node !== parent.body) {
1125
+ checkNode(node);
1126
+ }
1127
+ },
1128
+ "PropertyDefinition[key.name=/[$]+$/] > Identifier": (node) => {
1129
+ checkNode(node, node.parent);
1130
+ },
1131
+ "FunctionDeclaration > Identifier[name=/[$]+$/]": (node) => {
1132
+ const parent = node.parent;
1133
+ if (node === parent.id) {
1134
+ checkNode(node, parent);
1135
+ } else {
1136
+ checkNode(node);
1137
+ }
1138
+ },
1139
+ "FunctionExpression > Identifier[name=/[$]+$/]": (node) => {
1140
+ const parent = node.parent;
1141
+ if (node === parent.id) {
1142
+ checkNode(node, parent);
1143
+ } else {
1144
+ checkNode(node);
1145
+ }
1146
+ },
1147
+ "MethodDefinition[key.name=/[$]+$/]": (node) => {
1148
+ checkNode(node.key, node);
1149
+ },
1150
+ "ObjectExpression > Property[computed=false][key.name=/[$]+$/]": (node) => {
1151
+ checkNode(node.key);
1152
+ },
1153
+ "ObjectPattern > Property[value.name=/[$]+$/]": (node) => {
1154
+ checkNode(node.value);
1155
+ },
1156
+ "TSCallSignatureDeclaration > Identifier[name=/[$]+$/]": (node) => {
1157
+ checkNode(node);
1158
+ },
1159
+ "TSConstructSignatureDeclaration > Identifier[name=/[$]+$/]": (node) => {
1160
+ checkNode(node);
1161
+ },
1162
+ "TSParameterProperty > Identifier[name=/[$]+$/]": (node) => {
1163
+ checkNode(node);
1164
+ },
1165
+ "TSPropertySignature > Identifier[name=/[$]+$/]": (node) => {
1166
+ checkNode(node, node.parent);
1167
+ },
1168
+ "TSMethodSignature > Identifier[name=/[$]+$/]": (node) => {
1169
+ const parent = node.parent;
1170
+ if (node === parent.key) {
1171
+ checkNode(node, parent);
1172
+ } else {
1173
+ checkNode(node);
1174
+ }
1175
+ },
1176
+ "VariableDeclarator[id.name=/[$]+$/]": (node) => {
1177
+ checkNode(node.id, node.init ?? node);
1178
+ }
1179
+ };
1180
+ }
1181
+ });
1182
+
1183
+ const noIgnoredErrorRule = ruleCreator({
1184
+ defaultOptions: [],
1185
+ meta: {
1186
+ docs: {
1187
+ description: "Forbids the calling of `subscribe` without specifying an error handler.",
1188
+ requiresTypeChecking: true
1189
+ },
1190
+ messages: {
1191
+ forbidden: "Calling subscribe without an error handler is forbidden."
1192
+ },
1193
+ schema: [],
1194
+ type: "problem"
1195
+ },
1196
+ name: "no-ignored-error",
1197
+ create: (context) => {
1198
+ const { couldBeObservable, couldBeFunction } = getTypeServices(context);
1199
+ return {
1200
+ "CallExpression[arguments.length > 0] > MemberExpression > Identifier[name='subscribe']": (node) => {
1201
+ const memberExpression = node.parent;
1202
+ const callExpression = memberExpression.parent;
1203
+ if (callExpression.arguments.length < 2 && couldBeObservable(memberExpression.object) && couldBeFunction(callExpression.arguments[0])) {
1204
+ context.report({
1205
+ messageId: "forbidden",
1206
+ node
1207
+ });
1208
+ }
1209
+ }
1210
+ };
1211
+ }
1212
+ });
1213
+
1214
+ const noIgnoredNotifierRule = ruleCreator({
1215
+ defaultOptions: [],
1216
+ meta: {
1217
+ docs: {
1218
+ description: "Forbids observables not composed from the `repeatWhen` or `retryWhen` notifier.",
1219
+ recommended: true,
1220
+ requiresTypeChecking: true
1221
+ },
1222
+ messages: {
1223
+ forbidden: "Ignoring the notifier is forbidden."
1224
+ },
1225
+ schema: [],
1226
+ type: "problem"
1227
+ },
1228
+ name: "no-ignored-notifier",
1229
+ create: (context) => {
1230
+ const { couldBeMonoTypeOperatorFunction } = getTypeServices(context);
1231
+ const entries = [];
1232
+ function getEntry() {
1233
+ const { length, [length - 1]: entry } = entries;
1234
+ return entry;
1235
+ }
1236
+ return {
1237
+ "CallExpression[callee.name=/^(repeatWhen|retryWhen)$/]": (node) => {
1238
+ if (couldBeMonoTypeOperatorFunction(node)) {
1239
+ const [arg] = node.arguments;
1240
+ if (isArrowFunctionExpression(arg) || isFunctionExpression(arg)) {
1241
+ const [param] = arg.params;
1242
+ if (param) {
1243
+ entries.push({
1244
+ node,
1245
+ param,
1246
+ sightings: 0
1247
+ });
1248
+ } else {
1249
+ context.report({
1250
+ messageId: "forbidden",
1251
+ node: node.callee
1252
+ });
1253
+ }
1254
+ }
1255
+ }
1256
+ },
1257
+ "CallExpression[callee.name=/^(repeatWhen|retryWhen)$/]:exit": (node) => {
1258
+ const entry = getEntry();
1259
+ if (!entry) {
1260
+ return;
1261
+ }
1262
+ if (entry.node === node) {
1263
+ if (entry.sightings < 2) {
1264
+ context.report({
1265
+ messageId: "forbidden",
1266
+ node: node.callee
1267
+ });
1268
+ }
1269
+ entries.pop();
1270
+ }
1271
+ },
1272
+ "Identifier": (node) => {
1273
+ const entry = getEntry();
1274
+ if (!entry) {
1275
+ return;
1276
+ }
1277
+ if (node.name === entry.param.name) {
1278
+ ++entry.sightings;
1279
+ }
1280
+ }
1281
+ };
1282
+ }
1283
+ });
1284
+
1285
+ const noIgnoredObservableRule = ruleCreator({
1286
+ defaultOptions: [],
1287
+ meta: {
1288
+ docs: {
1289
+ description: "Forbids the ignoring of observables returned by functions.",
1290
+ requiresTypeChecking: true
1291
+ },
1292
+ messages: {
1293
+ forbidden: "Ignoring a returned Observable is forbidden."
1294
+ },
1295
+ schema: [],
1296
+ type: "problem"
1297
+ },
1298
+ name: "no-ignored-observable",
1299
+ create: (context) => {
1300
+ const { couldBeObservable } = getTypeServices(context);
1301
+ return {
1302
+ "ExpressionStatement > CallExpression": (node) => {
1303
+ if (couldBeObservable(node)) {
1304
+ context.report({
1305
+ messageId: "forbidden",
1306
+ node
1307
+ });
1308
+ }
1309
+ }
1310
+ };
1311
+ }
1312
+ });
1313
+
1314
+ const noIgnoredReplayBufferRule = ruleCreator({
1315
+ defaultOptions: [],
1316
+ meta: {
1317
+ docs: {
1318
+ description: "Forbids using `ReplaySubject`, `publishReplay` or `shareReplay` without specifying the buffer size.",
1319
+ recommended: true
1320
+ },
1321
+ messages: {
1322
+ forbidden: "Ignoring the buffer size is forbidden."
1323
+ },
1324
+ schema: [],
1325
+ type: "problem"
1326
+ },
1327
+ name: "no-ignored-replay-buffer",
1328
+ create: (context) => {
1329
+ function checkNode(node, { arguments: args }) {
1330
+ if (!args || args.length === 0) {
1331
+ context.report({
1332
+ messageId: "forbidden",
1333
+ node
1334
+ });
1335
+ }
1336
+ }
1337
+ return {
1338
+ "NewExpression > Identifier[name='ReplaySubject']": (node) => {
1339
+ const newExpression = node.parent;
1340
+ checkNode(node, newExpression);
1341
+ },
1342
+ "NewExpression > MemberExpression > Identifier[name='ReplaySubject']": (node) => {
1343
+ const memberExpression = node.parent;
1344
+ const newExpression = memberExpression.parent;
1345
+ checkNode(node, newExpression);
1346
+ },
1347
+ "CallExpression > Identifier[name=/^(publishReplay|shareReplay)$/]": (node) => {
1348
+ const callExpression = node.parent;
1349
+ checkNode(node, callExpression);
1350
+ }
1351
+ };
1352
+ }
1353
+ });
1354
+
1355
+ const noIgnoredSubscribeRule = ruleCreator({
1356
+ defaultOptions: [],
1357
+ meta: {
1358
+ docs: {
1359
+ description: "Forbids the calling of `subscribe` without specifying arguments.",
1360
+ requiresTypeChecking: true
1361
+ },
1362
+ messages: {
1363
+ forbidden: "Calling subscribe without arguments is forbidden."
1364
+ },
1365
+ schema: [],
1366
+ type: "problem"
1367
+ },
1368
+ name: "no-ignored-subscribe",
1369
+ create: (context) => {
1370
+ const { couldBeObservable, couldBeType } = getTypeServices(context);
1371
+ return {
1372
+ "CallExpression[arguments.length = 0][callee.property.name='subscribe']": (node) => {
1373
+ const callee = node.callee;
1374
+ if (couldBeObservable(callee.object) || couldBeType(callee.object, "Subscribable")) {
1375
+ context.report({
1376
+ messageId: "forbidden",
1377
+ node: callee.property
1378
+ });
1379
+ }
1380
+ }
1381
+ };
1382
+ }
1383
+ });
1384
+
1385
+ const noIgnoredSubscriptionRule = ruleCreator({
1386
+ defaultOptions: [],
1387
+ meta: {
1388
+ docs: {
1389
+ description: "Forbids ignoring the subscription returned by `subscribe`.",
1390
+ requiresTypeChecking: true
1391
+ },
1392
+ messages: {
1393
+ forbidden: "Ignoring returned subscriptions is forbidden."
1394
+ },
1395
+ schema: [],
1396
+ type: "problem"
1397
+ },
1398
+ name: "no-ignored-subscription",
1399
+ create: (context) => {
1400
+ const { couldBeObservable, couldBeType } = getTypeServices(context);
1401
+ return {
1402
+ "ExpressionStatement > CallExpression > MemberExpression[property.name='subscribe']": (node) => {
1403
+ if (couldBeObservable(node.object)) {
1404
+ const callExpression = node.parent;
1405
+ if (callExpression.arguments.length === 1 && couldBeType(callExpression.arguments[0], "Subscriber")) {
1406
+ return;
1407
+ }
1408
+ context.report({
1409
+ messageId: "forbidden",
1410
+ node: node.property
1411
+ });
1412
+ }
1413
+ }
1414
+ };
1415
+ }
1416
+ });
1417
+
1418
+ const noIgnoredTakewhileValueRule = ruleCreator({
1419
+ defaultOptions: [],
1420
+ meta: {
1421
+ docs: {
1422
+ description: "Forbids ignoring the value within `takeWhile`.",
1423
+ recommended: true
1424
+ },
1425
+ messages: {
1426
+ forbidden: "Ignoring the value within takeWhile is forbidden."
1427
+ },
1428
+ schema: [],
1429
+ type: "problem"
1430
+ },
1431
+ name: "no-ignored-takewhile-value",
1432
+ create: (context) => {
1433
+ function checkNode(expression) {
1434
+ const scope = context.sourceCode.getScope(expression);
1435
+ if (!isImport(scope, "takeWhile", /^rxjs\/?/)) {
1436
+ return;
1437
+ }
1438
+ let ignored = true;
1439
+ const [param] = expression.params;
1440
+ if (param) {
1441
+ if (isIdentifier(param)) {
1442
+ const variable = scope.variables.find(
1443
+ ({ name }) => name === param.name
1444
+ );
1445
+ if (variable && variable.references.length > 0) {
1446
+ ignored = false;
1447
+ }
1448
+ } else if (isArrayPattern(param)) {
1449
+ ignored = false;
1450
+ } else if (isObjectPattern(param)) {
1451
+ ignored = false;
1452
+ }
1453
+ }
1454
+ if (ignored) {
1455
+ context.report({
1456
+ messageId: "forbidden",
1457
+ node: expression
1458
+ });
1459
+ }
1460
+ }
1461
+ return {
1462
+ "CallExpression[callee.name='takeWhile'] > ArrowFunctionExpression": (node) => {
1463
+ checkNode(node);
1464
+ },
1465
+ "CallExpression[callee.name='takeWhile'] > FunctionExpression": (node) => {
1466
+ checkNode(node);
1467
+ }
1468
+ };
1469
+ }
1470
+ });
1471
+
1472
+ function isParenthesised(sourceCode, node) {
1473
+ const before = sourceCode.getTokenBefore(node);
1474
+ const after = sourceCode.getTokenAfter(node);
1475
+ return before && after && before.value === "(" && before.range[1] <= node.range[0] && after.value === ")" && after.range[0] >= node.range[1];
1476
+ }
1477
+ const defaultOptions$7 = [];
1478
+ const noImplicitAnyCatchRule = ruleCreator({
1479
+ defaultOptions: defaultOptions$7,
1480
+ meta: {
1481
+ docs: {
1482
+ description: "Forbids implicit `any` error parameters in `catchError` operators.",
1483
+ recommended: true,
1484
+ requiresTypeChecking: true
1485
+ },
1486
+ fixable: "code",
1487
+ hasSuggestions: true,
1488
+ messages: {
1489
+ explicitAny: "Explicit `any` in `catchError`.",
1490
+ implicitAny: "Implicit `any` in `catchError`.",
1491
+ narrowed: "Error type must be `unknown` or `any`.",
1492
+ suggestExplicitUnknown: "Use `unknown` instead, this will force you to explicitly and safely assert the type is correct."
1493
+ },
1494
+ schema: [
1495
+ {
1496
+ additionalProperties: false,
1497
+ properties: {
1498
+ allowExplicitAny: {
1499
+ type: "boolean"
1500
+ }
1501
+ },
1502
+ type: "object"
1503
+ }
1504
+ ],
1505
+ type: "suggestion"
1506
+ },
1507
+ name: "no-implicit-any-catch",
1508
+ create: (context) => {
1509
+ const [config = {}] = context.options;
1510
+ const { allowExplicitAny = false } = config;
1511
+ const { couldBeObservable } = getTypeServices(context);
1512
+ const sourceCode = context.sourceCode;
1513
+ function checkCallback(callback) {
1514
+ if (isArrowFunctionExpression(callback) || isFunctionExpression(callback)) {
1515
+ const [param] = callback.params;
1516
+ if (!param) {
1517
+ return;
1518
+ }
1519
+ if (hasTypeAnnotation(param)) {
1520
+ const { typeAnnotation } = param;
1521
+ const {
1522
+ typeAnnotation: { type }
1523
+ } = typeAnnotation;
1524
+ if (type === utils.AST_NODE_TYPES.TSAnyKeyword) {
1525
+ let fix = function(fixer) {
1526
+ return fixer.replaceText(typeAnnotation, ": unknown");
1527
+ };
1528
+ if (allowExplicitAny) {
1529
+ return;
1530
+ }
1531
+ context.report({
1532
+ fix,
1533
+ messageId: "explicitAny",
1534
+ node: param,
1535
+ suggest: [
1536
+ {
1537
+ messageId: "suggestExplicitUnknown",
1538
+ fix
1539
+ }
1540
+ ]
1541
+ });
1542
+ } else if (type !== utils.AST_NODE_TYPES.TSUnknownKeyword) {
1543
+ let fix = function(fixer) {
1544
+ return fixer.replaceText(typeAnnotation, ": unknown");
1545
+ };
1546
+ context.report({
1547
+ messageId: "narrowed",
1548
+ node: param,
1549
+ suggest: [
1550
+ {
1551
+ messageId: "suggestExplicitUnknown",
1552
+ fix
1553
+ }
1554
+ ]
1555
+ });
1556
+ }
1557
+ } else {
1558
+ let fix = function(fixer) {
1559
+ if (isParenthesised(sourceCode, param)) {
1560
+ return fixer.insertTextAfter(param, ": unknown");
1561
+ }
1562
+ return [
1563
+ fixer.insertTextBefore(param, "("),
1564
+ fixer.insertTextAfter(param, ": unknown)")
1565
+ ];
1566
+ };
1567
+ context.report({
1568
+ fix,
1569
+ messageId: "implicitAny",
1570
+ node: param,
1571
+ suggest: [
1572
+ {
1573
+ messageId: "suggestExplicitUnknown",
1574
+ fix
1575
+ }
1576
+ ]
1577
+ });
1578
+ }
1579
+ }
1580
+ }
1581
+ return {
1582
+ "CallExpression[callee.name='catchError']": (node) => {
1583
+ const [callback] = node.arguments;
1584
+ if (!callback) {
1585
+ return;
1586
+ }
1587
+ checkCallback(callback);
1588
+ },
1589
+ "CallExpression[callee.property.name='subscribe'],CallExpression[callee.name='tap']": (node) => {
1590
+ const { callee } = node;
1591
+ if (isMemberExpression(callee) && !couldBeObservable(callee.object)) {
1592
+ return;
1593
+ }
1594
+ const [observer, callback] = node.arguments;
1595
+ if (callback) {
1596
+ checkCallback(callback);
1597
+ } else if (observer && isObjectExpression(observer)) {
1598
+ const errorProperty = observer.properties.find(
1599
+ (property) => isProperty(property) && isIdentifier(property.key) && property.key.name === "error"
1600
+ );
1601
+ if (errorProperty) {
1602
+ checkCallback(errorProperty.value);
1603
+ }
1604
+ }
1605
+ }
1606
+ };
1607
+ }
1608
+ });
1609
+
1610
+ const noIndexRule = ruleCreator({
1611
+ defaultOptions: [],
1612
+ meta: {
1613
+ docs: {
1614
+ description: "Forbids the importation from index modules.",
1615
+ recommended: true
1616
+ },
1617
+ messages: {
1618
+ forbidden: "RxJS imports from index modules are forbidden."
1619
+ },
1620
+ schema: [],
1621
+ type: "problem"
1622
+ },
1623
+ name: "no-index",
1624
+ create: (context) => {
1625
+ return {
1626
+ [String.raw`ImportDeclaration Literal[value=/^rxjs(?:\u002f\w+)?\u002findex/]`]: (node) => {
1627
+ context.report({
1628
+ messageId: "forbidden",
1629
+ node
1630
+ });
1631
+ }
1632
+ };
1633
+ }
1634
+ });
1635
+
1636
+ const noInternalRule = ruleCreator({
1637
+ defaultOptions: [],
1638
+ meta: {
1639
+ docs: {
1640
+ description: "Forbids the importation of internals.",
1641
+ recommended: true
1642
+ },
1643
+ fixable: "code",
1644
+ hasSuggestions: true,
1645
+ messages: {
1646
+ forbidden: "RxJS imports from internal are forbidden.",
1647
+ suggest: "Import from a non-internal location."
1648
+ },
1649
+ schema: [],
1650
+ type: "problem"
1651
+ },
1652
+ name: "no-internal",
1653
+ create: (context) => {
1654
+ function getReplacement(location) {
1655
+ const match = /^\s*('|")/.exec(location);
1656
+ if (!match) {
1657
+ return void 0;
1658
+ }
1659
+ const [, quote] = match;
1660
+ if (/^['"]rxjs\/internal\/ajax/.test(location)) {
1661
+ return `${quote}rxjs/ajax${quote}`;
1662
+ }
1663
+ if (/^['"]rxjs\/internal\/observable\/dom\/fetch/.test(location)) {
1664
+ return `${quote}rxjs/fetch${quote}`;
1665
+ }
1666
+ if (/^['"]rxjs\/internal\/observable\/dom\/webSocket/i.test(location)) {
1667
+ return `${quote}rxjs/webSocket${quote}`;
1668
+ }
1669
+ if (/^['"]rxjs\/internal\/observable/.test(location)) {
1670
+ return `${quote}rxjs${quote}`;
1671
+ }
1672
+ if (/^['"]rxjs\/internal\/operators/.test(location)) {
1673
+ return `${quote}rxjs/operators${quote}`;
1674
+ }
1675
+ if (/^['"]rxjs\/internal\/scheduled/.test(location)) {
1676
+ return `${quote}rxjs${quote}`;
1677
+ }
1678
+ if (/^['"]rxjs\/internal\/scheduler/.test(location)) {
1679
+ return `${quote}rxjs${quote}`;
1680
+ }
1681
+ if (/^['"]rxjs\/internal\/testing/.test(location)) {
1682
+ return `${quote}rxjs/testing${quote}`;
1683
+ }
1684
+ return void 0;
1685
+ }
1686
+ return {
1687
+ [String.raw`ImportDeclaration Literal[value=/^rxjs\u002finternal/]`]: (node) => {
1688
+ const replacement = getReplacement(node.raw);
1689
+ if (replacement) {
1690
+ let fix = function(fixer) {
1691
+ return fixer.replaceText(node, replacement);
1692
+ };
1693
+ context.report({
1694
+ fix,
1695
+ messageId: "forbidden",
1696
+ node,
1697
+ suggest: [{ fix, messageId: "suggest" }]
1698
+ });
1699
+ } else {
1700
+ context.report({
1701
+ messageId: "forbidden",
1702
+ node
1703
+ });
1704
+ }
1705
+ }
1706
+ };
1707
+ }
1708
+ });
1709
+
1710
+ const noNestedSubscribeRule = ruleCreator({
1711
+ defaultOptions: [],
1712
+ meta: {
1713
+ docs: {
1714
+ description: "Forbids the calling of `subscribe` within a `subscribe` callback.",
1715
+ recommended: true,
1716
+ requiresTypeChecking: true
1717
+ },
1718
+ messages: {
1719
+ forbidden: "Nested subscribe calls are forbidden."
1720
+ },
1721
+ schema: [],
1722
+ type: "problem"
1723
+ },
1724
+ name: "no-nested-subscribe",
1725
+ create: (context) => {
1726
+ const { couldBeObservable, couldBeType } = getTypeServices(context);
1727
+ const argumentsMap = /* @__PURE__ */ new WeakMap();
1728
+ return {
1729
+ [`CallExpression > MemberExpression[property.name='subscribe']`]: (node) => {
1730
+ if (!couldBeObservable(node.object) && !couldBeType(node.object, "Subscribable")) {
1731
+ return;
1732
+ }
1733
+ const callExpression = node.parent;
1734
+ let parent = callExpression.parent;
1735
+ while (parent) {
1736
+ if (argumentsMap.has(parent)) {
1737
+ context.report({
1738
+ messageId: "forbidden",
1739
+ node: node.property
1740
+ });
1741
+ return;
1742
+ }
1743
+ parent = parent.parent;
1744
+ }
1745
+ for (const arg of callExpression.arguments) {
1746
+ argumentsMap.set(arg);
1747
+ }
1748
+ }
1749
+ };
1750
+ }
1751
+ });
1752
+
1753
+ const noRedundantNotifyRule = ruleCreator({
1754
+ defaultOptions: [],
1755
+ meta: {
1756
+ docs: {
1757
+ description: "Forbids redundant notifications from completed or errored observables.",
1758
+ recommended: true,
1759
+ requiresTypeChecking: true
1760
+ },
1761
+ messages: {
1762
+ forbidden: "Redundant notifications are forbidden."
1763
+ },
1764
+ schema: [],
1765
+ type: "problem"
1766
+ },
1767
+ name: "no-redundant-notify",
1768
+ create: (context) => {
1769
+ const sourceCode = context.sourceCode;
1770
+ const { couldBeType } = getTypeServices(context);
1771
+ return {
1772
+ "ExpressionStatement[expression.callee.property.name=/^(complete|error)$/] + ExpressionStatement[expression.callee.property.name=/^(next|complete|error)$/]": (node) => {
1773
+ const parent = node.parent;
1774
+ if (!parent) {
1775
+ return;
1776
+ }
1777
+ if (!isBlockStatement(parent) && !isProgram(parent)) {
1778
+ return;
1779
+ }
1780
+ const { body } = parent;
1781
+ const index = body.indexOf(node);
1782
+ const sibling = body[index - 1];
1783
+ if (getExpressionText(sibling, sourceCode) !== getExpressionText(node, sourceCode)) {
1784
+ return;
1785
+ }
1786
+ if (!isExpressionObserver(sibling, couldBeType) || !isExpressionObserver(node, couldBeType)) {
1787
+ return;
1788
+ }
1789
+ const { expression } = node;
1790
+ if (isCallExpression(expression)) {
1791
+ const { callee } = expression;
1792
+ if (isMemberExpression(callee)) {
1793
+ const { property } = callee;
1794
+ if (isIdentifier(property)) {
1795
+ context.report({
1796
+ messageId: "forbidden",
1797
+ node: property
1798
+ });
1799
+ }
1800
+ }
1801
+ }
1802
+ }
1803
+ };
1804
+ }
1805
+ });
1806
+ function getExpressionText(expressionStatement, sourceCode) {
1807
+ if (!isCallExpression(expressionStatement.expression)) {
1808
+ return void 0;
1809
+ }
1810
+ const callExpression = expressionStatement.expression;
1811
+ if (!isMemberExpression(callExpression.callee)) {
1812
+ return void 0;
1813
+ }
1814
+ const { object } = callExpression.callee;
1815
+ return sourceCode.getText(object);
1816
+ }
1817
+ function isExpressionObserver(expressionStatement, couldBeType) {
1818
+ if (!isCallExpression(expressionStatement.expression)) {
1819
+ return false;
1820
+ }
1821
+ const callExpression = expressionStatement.expression;
1822
+ if (!isMemberExpression(callExpression.callee)) {
1823
+ return false;
1824
+ }
1825
+ const { object } = callExpression.callee;
1826
+ return couldBeType(object, /^(Subject|Subscriber)$/);
1827
+ }
1828
+
1829
+ const defaultOptions$6 = [];
1830
+ const noSharereplayRule = ruleCreator({
1831
+ defaultOptions: defaultOptions$6,
1832
+ meta: {
1833
+ docs: {
1834
+ description: "Forbids using the `shareReplay` operator.",
1835
+ recommended: true
1836
+ },
1837
+ messages: {
1838
+ forbidden: "shareReplay is forbidden.",
1839
+ forbiddenWithoutConfig: "shareReplay is forbidden unless a config argument is passed."
1840
+ },
1841
+ schema: [
1842
+ {
1843
+ properties: {
1844
+ allowConfig: { type: "boolean" }
1845
+ },
1846
+ type: "object"
1847
+ }
1848
+ ],
1849
+ type: "problem"
1850
+ },
1851
+ name: "no-sharereplay",
1852
+ create: (context) => {
1853
+ const [config = {}] = context.options;
1854
+ const { allowConfig = true } = config;
1855
+ return {
1856
+ "CallExpression[callee.name='shareReplay']": (node) => {
1857
+ let report = true;
1858
+ if (allowConfig) {
1859
+ report = node.arguments.length !== 1 || node.arguments[0].type !== utils.AST_NODE_TYPES.ObjectExpression;
1860
+ }
1861
+ if (report) {
1862
+ context.report({
1863
+ messageId: allowConfig ? "forbiddenWithoutConfig" : "forbidden",
1864
+ node: node.callee
1865
+ });
1866
+ }
1867
+ }
1868
+ };
1869
+ }
1870
+ });
1871
+
1872
+ const noSubclassRule = ruleCreator({
1873
+ defaultOptions: [],
1874
+ meta: {
1875
+ docs: {
1876
+ description: "Forbids subclassing RxJS classes.",
1877
+ requiresTypeChecking: true
1878
+ },
1879
+ messages: {
1880
+ forbidden: "Subclassing RxJS classes is forbidden."
1881
+ },
1882
+ schema: [],
1883
+ type: "problem"
1884
+ },
1885
+ name: "no-subclass",
1886
+ create: (context) => {
1887
+ const { couldBeType } = getTypeServices(context);
1888
+ const queryNames = [
1889
+ "AsyncSubject",
1890
+ "BehaviorSubject",
1891
+ "Observable",
1892
+ "ReplaySubject",
1893
+ "Scheduler",
1894
+ "Subject",
1895
+ "Subscriber"
1896
+ ];
1897
+ return {
1898
+ [`ClassDeclaration[superClass.name=/^(${queryNames.join(
1899
+ "|"
1900
+ )})$/] > Identifier.superClass`]: (node) => {
1901
+ if (queryNames.some(
1902
+ (name) => couldBeType(node, name, { name: /[/\\]rxjs[/\\]/ })
1903
+ )) {
1904
+ context.report({
1905
+ messageId: "forbidden",
1906
+ node
1907
+ });
1908
+ }
1909
+ }
1910
+ };
1911
+ }
1912
+ });
1913
+
1914
+ const noSubjectUnsubscribeRule = ruleCreator({
1915
+ defaultOptions: [],
1916
+ meta: {
1917
+ docs: {
1918
+ description: "Forbids calling the `unsubscribe` method of a subject instance.",
1919
+ recommended: true,
1920
+ requiresTypeChecking: true
1921
+ },
1922
+ messages: {
1923
+ forbidden: "Calling unsubscribe on a subject is forbidden."
1924
+ },
1925
+ schema: [],
1926
+ type: "problem"
1927
+ },
1928
+ name: "no-subject-unsubscribe",
1929
+ create: (context) => {
1930
+ const { couldBeSubject, couldBeSubscription } = getTypeServices(context);
1931
+ return {
1932
+ "MemberExpression[property.name='unsubscribe']": (node) => {
1933
+ if (couldBeSubject(node.object)) {
1934
+ context.report({
1935
+ messageId: "forbidden",
1936
+ node: node.property
1937
+ });
1938
+ }
1939
+ },
1940
+ "CallExpression[callee.property.name='add'][arguments.length > 0]": (node) => {
1941
+ const memberExpression = node.callee;
1942
+ if (couldBeSubscription(memberExpression.object)) {
1943
+ const [arg] = node.arguments;
1944
+ if (couldBeSubject(arg)) {
1945
+ context.report({
1946
+ messageId: "forbidden",
1947
+ node: arg
1948
+ });
1949
+ }
1950
+ }
1951
+ }
1952
+ };
1953
+ }
1954
+ });
1955
+
1956
+ const noSubjectValueRule = ruleCreator({
1957
+ defaultOptions: [],
1958
+ meta: {
1959
+ docs: {
1960
+ description: "Forbids accessing the `value` property of a `BehaviorSubject` instance.",
1961
+ requiresTypeChecking: true
1962
+ },
1963
+ messages: {
1964
+ forbidden: "Accessing the value property of a BehaviorSubject is forbidden."
1965
+ },
1966
+ schema: [],
1967
+ type: "problem"
1968
+ },
1969
+ name: "no-subject-value",
1970
+ create: (context) => {
1971
+ const { couldBeBehaviorSubject } = getTypeServices(context);
1972
+ return {
1973
+ "Identifier[name=/^(value|getValue)$/]": (node) => {
1974
+ const parent = node.parent;
1975
+ if (!parent || !("object" in parent)) {
1976
+ return;
1977
+ }
1978
+ if (couldBeBehaviorSubject(parent.object)) {
1979
+ context.report({
1980
+ messageId: "forbidden",
1981
+ node
1982
+ });
1983
+ }
1984
+ }
1985
+ };
1986
+ }
1987
+ });
1988
+
1989
+ const noSubscribeHandlersRule = ruleCreator({
1990
+ defaultOptions: [],
1991
+ meta: {
1992
+ docs: {
1993
+ description: "Forbids the passing of handlers to `subscribe`.",
1994
+ requiresTypeChecking: true
1995
+ },
1996
+ messages: {
1997
+ forbidden: "Passing handlers to subscribe is forbidden."
1998
+ },
1999
+ schema: [],
2000
+ type: "problem"
2001
+ },
2002
+ name: "no-subscribe-handlers",
2003
+ create: (context) => {
2004
+ const { couldBeObservable, couldBeType } = getTypeServices(context);
2005
+ return {
2006
+ "CallExpression[arguments.length > 0][callee.property.name='subscribe']": (node) => {
2007
+ const callee = node.callee;
2008
+ if (couldBeObservable(callee.object) || couldBeType(callee.object, "Subscribable")) {
2009
+ context.report({
2010
+ messageId: "forbidden",
2011
+ node: callee.property
2012
+ });
2013
+ }
2014
+ }
2015
+ };
2016
+ }
2017
+ });
2018
+
2019
+ const noTapRule = ruleCreator({
2020
+ defaultOptions: [],
2021
+ meta: {
2022
+ deprecated: true,
2023
+ docs: {
2024
+ description: "Forbids the use of the `tap` operator."
2025
+ },
2026
+ messages: {
2027
+ forbidden: "The tap operator is forbidden."
2028
+ },
2029
+ replacedBy: ["ban-operators"],
2030
+ schema: [],
2031
+ type: "problem"
2032
+ },
2033
+ name: "no-tap",
2034
+ create: (context) => {
2035
+ return {
2036
+ [String.raw`ImportDeclaration[source.value=/^rxjs(\u002foperators)?$/] > ImportSpecifier[imported.name='tap']`]: (node) => {
2037
+ const { loc } = node;
2038
+ context.report({
2039
+ messageId: "forbidden",
2040
+ loc: {
2041
+ ...loc,
2042
+ end: {
2043
+ ...loc.start,
2044
+ column: loc.start.column + 3
2045
+ }
2046
+ }
2047
+ });
2048
+ }
2049
+ };
2050
+ }
2051
+ });
2052
+
2053
+ const noTopromiseRule = ruleCreator({
2054
+ defaultOptions: [],
2055
+ meta: {
2056
+ docs: {
2057
+ description: "Forbids the use of the `toPromise` method.",
2058
+ requiresTypeChecking: true
2059
+ },
2060
+ messages: {
2061
+ forbidden: "The toPromise method is forbidden."
2062
+ },
2063
+ schema: [],
2064
+ type: "problem"
2065
+ },
2066
+ name: "no-topromise",
2067
+ create: (context) => {
2068
+ const { couldBeObservable } = getTypeServices(context);
2069
+ return {
2070
+ [`MemberExpression[property.name="toPromise"]`]: (node) => {
2071
+ if (couldBeObservable(node.object)) {
2072
+ context.report({
2073
+ messageId: "forbidden",
2074
+ node: node.property
2075
+ });
2076
+ }
2077
+ }
2078
+ };
2079
+ }
2080
+ });
2081
+
2082
+ const noUnboundMethodsRule = ruleCreator({
2083
+ defaultOptions: [],
2084
+ meta: {
2085
+ docs: {
2086
+ description: "Forbids the passing of unbound methods.",
2087
+ recommended: true,
2088
+ requiresTypeChecking: true
2089
+ },
2090
+ messages: {
2091
+ forbidden: "Unbound methods are forbidden."
2092
+ },
2093
+ schema: [],
2094
+ type: "problem"
2095
+ },
2096
+ name: "no-unbound-methods",
2097
+ create: (context) => {
2098
+ const { couldBeObservable, couldBeSubscription, getType } = getTypeServices(context);
2099
+ const nodeMap = /* @__PURE__ */ new WeakMap();
2100
+ function mapArguments(node) {
2101
+ node.arguments.filter(isMemberExpression).forEach((arg) => {
2102
+ const argType = getType(arg);
2103
+ if (argType.getCallSignatures().length > 0) {
2104
+ nodeMap.set(arg);
2105
+ }
2106
+ });
2107
+ }
2108
+ function isObservableOrSubscription(node, action) {
2109
+ if (!isMemberExpression(node.callee)) {
2110
+ return;
2111
+ }
2112
+ if (couldBeObservable(node.callee.object) || couldBeSubscription(node.callee.object)) {
2113
+ action(node);
2114
+ }
2115
+ }
2116
+ return {
2117
+ "CallExpression[callee.property.name='pipe']": (node) => {
2118
+ isObservableOrSubscription(node, ({ arguments: args }) => {
2119
+ args.filter(isCallExpression).forEach(mapArguments);
2120
+ });
2121
+ },
2122
+ "CallExpression[callee.property.name=/^(add|subscribe)$/]": (node) => {
2123
+ isObservableOrSubscription(node, mapArguments);
2124
+ },
2125
+ "NewExpression[callee.name='Subscription']": mapArguments,
2126
+ "ThisExpression": (node) => {
2127
+ let parent = node.parent;
2128
+ while (parent) {
2129
+ if (nodeMap.has(parent)) {
2130
+ context.report({
2131
+ messageId: "forbidden",
2132
+ node: parent
2133
+ });
2134
+ return;
2135
+ }
2136
+ parent = parent.parent;
2137
+ }
2138
+ }
2139
+ };
2140
+ }
2141
+ });
2142
+
2143
+ const defaultOptions$5 = [];
2144
+ const noUnsafeCatchRule = ruleCreator({
2145
+ defaultOptions: defaultOptions$5,
2146
+ meta: {
2147
+ docs: {
2148
+ description: "Forbids unsafe `catchError` usage in effects and epics.",
2149
+ requiresTypeChecking: true
2150
+ },
2151
+ messages: {
2152
+ forbidden: "Unsafe catchError usage in effects and epics are forbidden."
2153
+ },
2154
+ schema: [
2155
+ {
2156
+ properties: {
2157
+ observable: { type: "string" }
2158
+ },
2159
+ type: "object",
2160
+ description: commonTags.stripIndent`
2161
+ An optional object with an optional \`observable\` property.
2162
+ The property can be specified as a regular expression string and is used to identify the action observables from which effects and epics are composed.`
2163
+ }
2164
+ ],
2165
+ type: "problem"
2166
+ },
2167
+ name: "no-unsafe-catch",
2168
+ create: (context) => {
2169
+ const invalidOperatorsRegExp = /^(catchError)$/;
2170
+ const [config = {}] = context.options;
2171
+ const { observable = defaultObservable } = config;
2172
+ const observableRegExp = new RegExp(observable);
2173
+ const { couldBeObservable } = getTypeServices(context);
2174
+ function isUnsafe([arg]) {
2175
+ if (arg && (isFunctionDeclaration(arg) || isArrowFunctionExpression(arg))) {
2176
+ return arg.params.length < 2;
2177
+ }
2178
+ return false;
2179
+ }
2180
+ function checkNode(node) {
2181
+ if (!node.arguments || !couldBeObservable(node)) {
2182
+ return;
2183
+ }
2184
+ node.arguments.forEach((arg) => {
2185
+ if (isCallExpression(arg) && isIdentifier(arg.callee)) {
2186
+ if (invalidOperatorsRegExp.test(arg.callee.name) && isUnsafe(arg.arguments)) {
2187
+ context.report({
2188
+ messageId: "forbidden",
2189
+ node: arg.callee
2190
+ });
2191
+ }
2192
+ }
2193
+ });
2194
+ }
2195
+ return {
2196
+ [`CallExpression[callee.property.name='pipe'][callee.object.name=${observableRegExp}]`]: checkNode,
2197
+ [`CallExpression[callee.property.name='pipe'][callee.object.property.name=${observableRegExp}]`]: checkNode
2198
+ };
2199
+ }
2200
+ });
2201
+
2202
+ const defaultOptions$4 = [];
2203
+ const noUnsafeFirstRule = ruleCreator({
2204
+ defaultOptions: defaultOptions$4,
2205
+ meta: {
2206
+ docs: {
2207
+ description: "Forbids unsafe `first`/`take` usage in effects and epics.",
2208
+ requiresTypeChecking: true
2209
+ },
2210
+ messages: {
2211
+ forbidden: "Unsafe first and take usage in effects and epics are forbidden."
2212
+ },
2213
+ schema: [
2214
+ {
2215
+ properties: {
2216
+ observable: { type: "string" }
2217
+ },
2218
+ type: "object",
2219
+ description: commonTags.stripIndent`
2220
+ An optional object with an optional \`observable\` property.
2221
+ The property can be specified as a regular expression string and is used to identify the action observables from which effects and epics are composed.`
2222
+ }
2223
+ ],
2224
+ type: "problem"
2225
+ },
2226
+ name: "no-unsafe-first",
2227
+ create: (context) => {
2228
+ const invalidOperatorsRegExp = /^(take|first)$/;
2229
+ const [config = {}] = context.options;
2230
+ const { observable = defaultObservable } = config;
2231
+ const observableRegExp = new RegExp(observable);
2232
+ const { couldBeObservable } = getTypeServices(context);
2233
+ const nodes = [];
2234
+ function checkNode(node) {
2235
+ if (!node.arguments || !couldBeObservable(node)) {
2236
+ return;
2237
+ }
2238
+ node.arguments.forEach((arg) => {
2239
+ if (isCallExpression(arg) && isIdentifier(arg.callee)) {
2240
+ if (invalidOperatorsRegExp.test(arg.callee.name)) {
2241
+ context.report({
2242
+ messageId: "forbidden",
2243
+ node: arg.callee
2244
+ });
2245
+ }
2246
+ }
2247
+ });
2248
+ }
2249
+ return {
2250
+ [`CallExpression[callee.property.name='pipe'][callee.object.name=${observableRegExp}]`]: (node) => {
2251
+ if (nodes.push(node) === 1) {
2252
+ checkNode(node);
2253
+ }
2254
+ },
2255
+ [`CallExpression[callee.property.name='pipe'][callee.object.name=${observableRegExp}]:exit`]: () => {
2256
+ nodes.pop();
2257
+ },
2258
+ [`CallExpression[callee.property.name='pipe'][callee.object.property.name=${observableRegExp}]`]: (node) => {
2259
+ if (nodes.push(node) === 1) {
2260
+ checkNode(node);
2261
+ }
2262
+ },
2263
+ [`CallExpression[callee.property.name='pipe'][callee.object.property.name=${observableRegExp}]:exit`]: () => {
2264
+ nodes.pop();
2265
+ }
2266
+ };
2267
+ }
2268
+ });
2269
+
2270
+ const noUnsafeSubjectNext = ruleCreator({
2271
+ defaultOptions: [],
2272
+ meta: {
2273
+ docs: {
2274
+ description: "Forbids unsafe optional `next` calls.",
2275
+ recommended: true,
2276
+ requiresTypeChecking: true
2277
+ },
2278
+ messages: {
2279
+ forbidden: "Unsafe optional next calls are forbidden."
2280
+ },
2281
+ schema: [],
2282
+ type: "problem"
2283
+ },
2284
+ name: "no-unsafe-subject-next",
2285
+ create: (context) => {
2286
+ const { getType, typeChecker } = getTypeServices(context);
2287
+ return {
2288
+ [`CallExpression[callee.property.name='next']`]: (node) => {
2289
+ if (node.arguments.length === 0 && isMemberExpression(node.callee)) {
2290
+ const type = getType(node.callee.object);
2291
+ if (tsutils.isReferenceType(type) && tsutils.couldBeType(type, "Subject")) {
2292
+ const [typeArg] = typeChecker.getTypeArguments(type);
2293
+ if (tsutils__namespace$1.isTypeFlagSet(typeArg, ts__namespace.TypeFlags.Any)) {
2294
+ return;
2295
+ }
2296
+ if (tsutils__namespace$1.isTypeFlagSet(typeArg, ts__namespace.TypeFlags.Unknown)) {
2297
+ return;
2298
+ }
2299
+ if (tsutils__namespace$1.isTypeFlagSet(typeArg, ts__namespace.TypeFlags.Void)) {
2300
+ return;
2301
+ }
2302
+ if (tsutils.isUnionType(typeArg) && typeArg.types.some(
2303
+ (t) => tsutils__namespace$1.isTypeFlagSet(t, ts__namespace.TypeFlags.Void)
2304
+ )) {
2305
+ return;
2306
+ }
2307
+ context.report({
2308
+ messageId: "forbidden",
2309
+ node: node.callee.property
2310
+ });
2311
+ }
2312
+ }
2313
+ }
2314
+ };
2315
+ }
2316
+ });
2317
+
2318
+ const defaultOptions$3 = [];
2319
+ const noUnsafeSwitchmapRule = ruleCreator({
2320
+ defaultOptions: defaultOptions$3,
2321
+ meta: {
2322
+ docs: {
2323
+ description: "Forbids unsafe `switchMap` usage in effects and epics.",
2324
+ requiresTypeChecking: true
2325
+ },
2326
+ messages: {
2327
+ forbidden: "Unsafe switchMap usage in effects and epics is forbidden."
2328
+ },
2329
+ schema: [
2330
+ {
2331
+ properties: {
2332
+ allow: {
2333
+ oneOf: [
2334
+ { type: "string" },
2335
+ { type: "array", items: { type: "string" } }
2336
+ ]
2337
+ },
2338
+ disallow: {
2339
+ oneOf: [
2340
+ { type: "string" },
2341
+ { type: "array", items: { type: "string" } }
2342
+ ]
2343
+ },
2344
+ observable: {
2345
+ oneOf: [
2346
+ { type: "string" },
2347
+ { type: "array", items: { type: "string" } }
2348
+ ]
2349
+ }
2350
+ },
2351
+ type: "object",
2352
+ description: commonTags.stripIndent`
2353
+ An optional object with optional \`allow\`, \`disallow\` and \`observable\` properties.
2354
+ The properties can be specified as regular expression strings or as arrays of words.
2355
+ The \`allow\` or \`disallow\` properties are mutually exclusive. Whether or not
2356
+ \`switchMap\` is allowed will depend upon the matching of action types with \`allow\` or \`disallow\`.
2357
+ The \`observable\` property is used to identify the action observables from which effects and epics are composed.
2358
+ `
2359
+ }
2360
+ ],
2361
+ type: "problem"
2362
+ },
2363
+ name: "no-unsafe-switchmap",
2364
+ create: (context) => {
2365
+ const defaultDisallow = [
2366
+ "add",
2367
+ "create",
2368
+ "delete",
2369
+ "post",
2370
+ "put",
2371
+ "remove",
2372
+ "set",
2373
+ "update"
2374
+ ];
2375
+ let allowRegExp;
2376
+ let disallowRegExp;
2377
+ let observableRegExp;
2378
+ const [config = {}] = context.options;
2379
+ if (config.allow || config.disallow) {
2380
+ allowRegExp = createRegExpForWords(config.allow ?? []);
2381
+ disallowRegExp = createRegExpForWords(config.disallow ?? []);
2382
+ observableRegExp = new RegExp(config.observable ?? defaultObservable);
2383
+ } else {
2384
+ allowRegExp = void 0;
2385
+ disallowRegExp = createRegExpForWords(defaultDisallow);
2386
+ observableRegExp = new RegExp(defaultObservable);
2387
+ }
2388
+ const { couldBeObservable } = getTypeServices(context);
2389
+ function shouldDisallow(args) {
2390
+ const names = args.map((arg) => {
2391
+ if (isLiteral(arg) && typeof arg.value === "string") {
2392
+ return arg.value;
2393
+ }
2394
+ if (isIdentifier(arg)) {
2395
+ return arg.name;
2396
+ }
2397
+ if (isMemberExpression(arg) && isIdentifier(arg.property)) {
2398
+ return arg.property.name;
2399
+ }
2400
+ return "";
2401
+ }).map((name) => decamelize__default(name));
2402
+ if (allowRegExp) {
2403
+ return !names.every((name) => allowRegExp?.test(name));
2404
+ }
2405
+ if (disallowRegExp) {
2406
+ return names.some((name) => disallowRegExp?.test(name));
2407
+ }
2408
+ return false;
2409
+ }
2410
+ function checkNode(node) {
2411
+ if (!node.arguments || !couldBeObservable(node)) {
2412
+ return;
2413
+ }
2414
+ const hasUnsafeOfType = node.arguments.some((arg) => {
2415
+ if (isCallExpression(arg) && isIdentifier(arg.callee) && arg.callee.name === "ofType") {
2416
+ return shouldDisallow(arg.arguments);
2417
+ }
2418
+ return false;
2419
+ });
2420
+ if (!hasUnsafeOfType) {
2421
+ return;
2422
+ }
2423
+ node.arguments.forEach((arg) => {
2424
+ if (isCallExpression(arg) && isIdentifier(arg.callee) && arg.callee.name === "switchMap") {
2425
+ context.report({
2426
+ messageId: "forbidden",
2427
+ node: arg.callee
2428
+ });
2429
+ }
2430
+ });
2431
+ }
2432
+ return {
2433
+ [`CallExpression[callee.property.name='pipe'][callee.object.name=${observableRegExp}]`]: checkNode,
2434
+ [`CallExpression[callee.property.name='pipe'][callee.object.property.name=${observableRegExp}]`]: checkNode
2435
+ };
2436
+ }
2437
+ });
2438
+
2439
+ const defaultOptions$2 = [];
2440
+ const noUnsafeTakeuntilRule = ruleCreator({
2441
+ defaultOptions: defaultOptions$2,
2442
+ meta: {
2443
+ docs: {
2444
+ description: "Forbids the application of operators after `takeUntil`.",
2445
+ recommended: true,
2446
+ requiresTypeChecking: true
2447
+ },
2448
+ messages: {
2449
+ forbidden: "Applying operators after takeUntil is forbidden."
2450
+ },
2451
+ schema: [
2452
+ {
2453
+ properties: {
2454
+ alias: { type: "array", items: { type: "string" } },
2455
+ allow: { type: "array", items: { type: "string" } }
2456
+ },
2457
+ type: "object",
2458
+ description: commonTags.stripIndent`
2459
+ An optional object with optional \`alias\` and \`allow\` properties.
2460
+ The \`alias\` property is an array containing the names of operators that aliases for \`takeUntil\`.
2461
+ The \`allow\` property is an array containing the names of the operators that are allowed to follow \`takeUntil\`.`
2462
+ }
2463
+ ],
2464
+ type: "problem"
2465
+ },
2466
+ name: "no-unsafe-takeuntil",
2467
+ create: (context) => {
2468
+ let checkedOperatorsRegExp = /^takeUntil$/;
2469
+ const allowedOperators = [
2470
+ "count",
2471
+ "defaultIfEmpty",
2472
+ "endWith",
2473
+ "every",
2474
+ "finalize",
2475
+ "finally",
2476
+ "isEmpty",
2477
+ "last",
2478
+ "max",
2479
+ "min",
2480
+ "publish",
2481
+ "publishBehavior",
2482
+ "publishLast",
2483
+ "publishReplay",
2484
+ "reduce",
2485
+ "share",
2486
+ "shareReplay",
2487
+ "skipLast",
2488
+ "takeLast",
2489
+ "throwIfEmpty",
2490
+ "toArray"
2491
+ ];
2492
+ const [config = {}] = context.options;
2493
+ const { alias, allow = allowedOperators } = config;
2494
+ if (alias) {
2495
+ checkedOperatorsRegExp = new RegExp(
2496
+ `^(${alias.concat("takeUntil").join("|")})$`
2497
+ );
2498
+ }
2499
+ const { couldBeObservable } = getTypeServices(context);
2500
+ function checkNode(node) {
2501
+ const pipeCallExpression = node.parent;
2502
+ if (!pipeCallExpression.arguments || !couldBeObservable(pipeCallExpression)) {
2503
+ return;
2504
+ }
2505
+ pipeCallExpression.arguments.reduceRight((state, arg) => {
2506
+ if (state === "taken") {
2507
+ return state;
2508
+ }
2509
+ if (!isCallExpression(arg)) {
2510
+ return "disallowed";
2511
+ }
2512
+ let operatorName;
2513
+ if (isIdentifier(arg.callee)) {
2514
+ operatorName = arg.callee.name;
2515
+ } else if (isMemberExpression(arg.callee) && isIdentifier(arg.callee.property)) {
2516
+ operatorName = arg.callee.property.name;
2517
+ } else {
2518
+ return "disallowed";
2519
+ }
2520
+ if (checkedOperatorsRegExp.test(operatorName)) {
2521
+ if (state === "disallowed") {
2522
+ context.report({
2523
+ messageId: "forbidden",
2524
+ node: arg.callee
2525
+ });
2526
+ }
2527
+ return "taken";
2528
+ }
2529
+ if (!allow.includes(operatorName)) {
2530
+ return "disallowed";
2531
+ }
2532
+ return state;
2533
+ }, "allowed");
2534
+ }
2535
+ return {
2536
+ [`CallExpression[callee.property.name='pipe'] > CallExpression[callee.name=${checkedOperatorsRegExp}]`]: checkNode,
2537
+ [`CallExpression[callee.property.name='pipe'] > CallExpression[callee.property.name=${checkedOperatorsRegExp}]`]: checkNode
2538
+ };
2539
+ }
2540
+ });
2541
+
2542
+ const defaultOptions$1 = [];
2543
+ const preferObserverRule = ruleCreator({
2544
+ defaultOptions: defaultOptions$1,
2545
+ meta: {
2546
+ docs: {
2547
+ description: "Forbids the passing separate handlers to `subscribe` and `tap`.",
2548
+ requiresTypeChecking: true
2549
+ },
2550
+ fixable: "code",
2551
+ hasSuggestions: true,
2552
+ messages: {
2553
+ forbidden: "Passing separate handlers is forbidden; pass an observer instead."
2554
+ },
2555
+ schema: [
2556
+ {
2557
+ properties: {
2558
+ allowNext: { type: "boolean" }
2559
+ },
2560
+ type: "object"
2561
+ }
2562
+ ],
2563
+ type: "problem"
2564
+ },
2565
+ name: "prefer-observer",
2566
+ create: (context) => {
2567
+ const { couldBeFunction, couldBeObservable } = getTypeServices(context);
2568
+ const [config = {}] = context.options;
2569
+ const { allowNext = true } = config;
2570
+ function checkArgs(callExpression, reportNode) {
2571
+ const { arguments: args, callee } = callExpression;
2572
+ if (isMemberExpression(callee) && !couldBeObservable(callee.object)) {
2573
+ return;
2574
+ }
2575
+ function* fix(fixer) {
2576
+ const sourceCode = context.sourceCode;
2577
+ const [nextArg, errorArg, completeArg] = args;
2578
+ const nextArgText = nextArg ? sourceCode.getText(nextArg) : "";
2579
+ const errorArgText = errorArg ? sourceCode.getText(errorArg) : "";
2580
+ const completeArgText = completeArg ? sourceCode.getText(completeArg) : "";
2581
+ let observer = "{";
2582
+ if (nextArgText && nextArgText !== "undefined" && nextArgText !== "null") {
2583
+ observer += ` next: ${nextArgText}${isValidArgText(errorArgText) || isValidArgText(completeArgText) ? "," : ""}`;
2584
+ }
2585
+ if (errorArgText && errorArgText !== "undefined" && errorArgText !== "null") {
2586
+ observer += ` error: ${errorArgText}${isValidArgText(completeArgText) ? "," : ""}`;
2587
+ }
2588
+ if (completeArgText && completeArgText !== "undefined" && completeArgText !== "null") {
2589
+ observer += ` complete: ${completeArgText}`;
2590
+ }
2591
+ observer += " }";
2592
+ yield fixer.replaceText(callExpression.arguments[0], observer);
2593
+ const [, start] = callExpression.arguments[0].range;
2594
+ const [, end] = callExpression.arguments[callExpression.arguments.length - 1].range;
2595
+ yield fixer.removeRange([start, end]);
2596
+ }
2597
+ if (args.length > 1) {
2598
+ context.report({
2599
+ messageId: "forbidden",
2600
+ node: reportNode,
2601
+ fix,
2602
+ suggest: [
2603
+ {
2604
+ messageId: "forbidden",
2605
+ fix
2606
+ }
2607
+ ]
2608
+ });
2609
+ } else if (args.length === 1 && !allowNext) {
2610
+ const [arg] = args;
2611
+ if (isArrowFunctionExpression(arg) || isFunctionExpression(arg) || couldBeFunction(arg)) {
2612
+ context.report({
2613
+ messageId: "forbidden",
2614
+ node: reportNode,
2615
+ fix,
2616
+ suggest: [
2617
+ {
2618
+ messageId: "forbidden",
2619
+ fix
2620
+ }
2621
+ ]
2622
+ });
2623
+ }
2624
+ }
2625
+ }
2626
+ return {
2627
+ "CallExpression[callee.property.name='pipe'] > CallExpression[callee.name='tap']": (node) => {
2628
+ checkArgs(node, node.callee);
2629
+ },
2630
+ "CallExpression[callee.property.name='subscribe']": (node) => {
2631
+ checkArgs(node, node.callee.property);
2632
+ }
2633
+ };
2634
+ }
2635
+ });
2636
+ function isValidArgText(argText) {
2637
+ return argText && argText !== "undefined" && argText !== "null";
2638
+ }
2639
+
2640
+ const defaultOptions = [];
2641
+ const suffixSubjectsRule = ruleCreator({
2642
+ defaultOptions,
2643
+ meta: {
2644
+ docs: {
2645
+ description: "Enforces the use of a suffix in subject identifiers.",
2646
+ requiresTypeChecking: true
2647
+ },
2648
+ messages: {
2649
+ forbidden: `Subject identifiers must end with "{{suffix}}".`
2650
+ },
2651
+ schema: [
2652
+ {
2653
+ properties: {
2654
+ parameters: { type: "boolean" },
2655
+ properties: { type: "boolean" },
2656
+ suffix: { type: "string" },
2657
+ types: { type: "object" },
2658
+ variables: { type: "boolean" }
2659
+ },
2660
+ type: "object"
2661
+ }
2662
+ ],
2663
+ type: "problem"
2664
+ },
2665
+ name: "suffix-subjects",
2666
+ create: (context) => {
2667
+ const { esTreeNodeToTSNodeMap } = utils.ESLintUtils.getParserServices(context);
2668
+ const { couldBeType } = getTypeServices(context);
2669
+ const [config = {}] = context.options;
2670
+ const validate = {
2671
+ parameters: true,
2672
+ properties: true,
2673
+ variables: true,
2674
+ ...config
2675
+ };
2676
+ const types = [];
2677
+ if (config.types) {
2678
+ Object.entries(config.types).forEach(
2679
+ ([key, validate2]) => {
2680
+ types.push({ regExp: new RegExp(key), validate: validate2 });
2681
+ }
2682
+ );
2683
+ } else {
2684
+ types.push({
2685
+ regExp: /^EventEmitter$/,
2686
+ validate: false
2687
+ });
2688
+ }
2689
+ const { suffix = "Subject" } = config;
2690
+ const suffixRegex = new RegExp(
2691
+ String.raw`${escapeRegExp(suffix)}\$?$`,
2692
+ "i"
2693
+ );
2694
+ function checkNode(nameNode, typeNode) {
2695
+ const tsNode = esTreeNodeToTSNodeMap.get(nameNode);
2696
+ const text = tsNode.getText();
2697
+ if (!suffixRegex.test(text) && couldBeType(typeNode || nameNode, "Subject")) {
2698
+ for (const type of types) {
2699
+ const { regExp, validate: validate2 } = type;
2700
+ if (couldBeType(typeNode || nameNode, regExp) && !validate2) {
2701
+ return;
2702
+ }
2703
+ }
2704
+ context.report({
2705
+ data: { suffix },
2706
+ loc: getLoc(tsNode),
2707
+ messageId: "forbidden"
2708
+ });
2709
+ }
2710
+ }
2711
+ return {
2712
+ "ArrayPattern > Identifier": (node) => {
2713
+ const found = findParent(
2714
+ node,
2715
+ "ArrowFunctionExpression",
2716
+ "FunctionDeclaration",
2717
+ "FunctionExpression",
2718
+ "VariableDeclarator"
2719
+ );
2720
+ if (!found) {
2721
+ return;
2722
+ }
2723
+ if (!validate.variables && found.type === utils.AST_NODE_TYPES.VariableDeclarator) {
2724
+ return;
2725
+ }
2726
+ if (!validate.parameters) {
2727
+ return;
2728
+ }
2729
+ checkNode(node);
2730
+ },
2731
+ "ArrowFunctionExpression > Identifier": (node) => {
2732
+ if (validate.parameters) {
2733
+ const parent = node.parent;
2734
+ if (node !== parent.body) {
2735
+ checkNode(node);
2736
+ }
2737
+ }
2738
+ },
2739
+ "PropertyDefinition[computed=false]": (node) => {
2740
+ const anyNode = node;
2741
+ if (validate.properties) {
2742
+ checkNode(anyNode.key);
2743
+ }
2744
+ },
2745
+ "FunctionDeclaration > Identifier": (node) => {
2746
+ if (validate.parameters) {
2747
+ const parent = node.parent;
2748
+ if (node !== parent.id) {
2749
+ checkNode(node);
2750
+ }
2751
+ }
2752
+ },
2753
+ "FunctionExpression > Identifier": (node) => {
2754
+ if (validate.parameters) {
2755
+ const parent = node.parent;
2756
+ if (node !== parent.id) {
2757
+ checkNode(node);
2758
+ }
2759
+ }
2760
+ },
2761
+ "MethodDefinition[kind='get'][computed=false]": (node) => {
2762
+ if (validate.properties) {
2763
+ checkNode(node.key, node);
2764
+ }
2765
+ },
2766
+ "MethodDefinition[kind='set'][computed=false]": (node) => {
2767
+ if (validate.properties) {
2768
+ checkNode(node.key, node);
2769
+ }
2770
+ },
2771
+ "ObjectExpression > Property[computed=false] > Identifier": (node) => {
2772
+ if (validate.properties) {
2773
+ const parent = node.parent;
2774
+ if (node === parent.key) {
2775
+ checkNode(node);
2776
+ }
2777
+ }
2778
+ },
2779
+ "ObjectPattern > Property > Identifier": (node) => {
2780
+ const found = findParent(
2781
+ node,
2782
+ "ArrowFunctionExpression",
2783
+ "FunctionDeclaration",
2784
+ "FunctionExpression",
2785
+ "VariableDeclarator"
2786
+ );
2787
+ if (!found) {
2788
+ return;
2789
+ }
2790
+ if (!validate.variables && found.type === utils.AST_NODE_TYPES.VariableDeclarator) {
2791
+ return;
2792
+ }
2793
+ if (!validate.parameters) {
2794
+ return;
2795
+ }
2796
+ const parent = node.parent;
2797
+ if (node === parent.value) {
2798
+ checkNode(node);
2799
+ }
2800
+ },
2801
+ "TSCallSignatureDeclaration > Identifier": (node) => {
2802
+ if (validate.parameters) {
2803
+ checkNode(node);
2804
+ }
2805
+ },
2806
+ "TSConstructSignatureDeclaration > Identifier": (node) => {
2807
+ if (validate.parameters) {
2808
+ checkNode(node);
2809
+ }
2810
+ },
2811
+ "TSMethodSignature > Identifier": (node) => {
2812
+ if (validate.parameters) {
2813
+ checkNode(node);
2814
+ }
2815
+ },
2816
+ "TSParameterProperty > Identifier": (node) => {
2817
+ if (validate.parameters || validate.properties) {
2818
+ checkNode(node);
2819
+ }
2820
+ },
2821
+ "TSPropertySignature[computed=false]": (node) => {
2822
+ if (validate.properties) {
2823
+ checkNode(node.key);
2824
+ }
2825
+ },
2826
+ "VariableDeclarator > Identifier": (node) => {
2827
+ const parent = node.parent;
2828
+ if (validate.variables && node === parent.id) {
2829
+ checkNode(node);
2830
+ }
2831
+ }
2832
+ };
2833
+ }
2834
+ });
2835
+
2836
+ const throwErrorRule = ruleCreator({
2837
+ defaultOptions: [],
2838
+ meta: {
2839
+ docs: {
2840
+ description: "Enforces the passing of `Error` values to error notifications.",
2841
+ requiresTypeChecking: true
2842
+ },
2843
+ messages: {
2844
+ forbidden: "Passing non-Error values are forbidden."
2845
+ },
2846
+ schema: [],
2847
+ type: "problem"
2848
+ },
2849
+ name: "throw-error",
2850
+ create: (context) => {
2851
+ const { esTreeNodeToTSNodeMap, program } = utils.ESLintUtils.getParserServices(context);
2852
+ const { couldBeObservable, getType } = getTypeServices(context);
2853
+ function checkNode(node) {
2854
+ let type = getType(node);
2855
+ if (tsutils.couldBeFunction(type)) {
2856
+ const tsNode = esTreeNodeToTSNodeMap.get(node);
2857
+ const annotation = tsNode.type;
2858
+ const body = tsNode.body;
2859
+ type = program.getTypeChecker().getTypeAtLocation(annotation ?? body);
2860
+ }
2861
+ if (!tsutils.isAny(type) && !tsutils.isUnknown(type) && !tsutils.couldBeType(type, /^(Error|DOMException)$/)) {
2862
+ context.report({
2863
+ messageId: "forbidden",
2864
+ node
2865
+ });
2866
+ }
2867
+ }
2868
+ return {
2869
+ "ThrowStatement > *": checkNode,
2870
+ "CallExpression[callee.name='throwError']": (node) => {
2871
+ if (couldBeObservable(node)) {
2872
+ const [arg] = node.arguments;
2873
+ if (arg) {
2874
+ checkNode(arg);
2875
+ }
2876
+ }
2877
+ }
2878
+ };
2879
+ }
2880
+ });
2881
+
2882
+ const plugin = {
2883
+ meta: { name, version },
2884
+ rules: {
2885
+ "ban-observables": banObservablesRule,
2886
+ "ban-operators": banOperatorsRule,
2887
+ "finnish": finnishRule,
2888
+ "just": justRule,
2889
+ "macro": macroRule,
2890
+ "no-async-subscribe": noAsyncSubscribeRule,
2891
+ "no-compat": noCompatRule,
2892
+ "no-connectable": noConnectableRule,
2893
+ "no-create": noCreateRule,
2894
+ "no-cyclic-action": noCyclicActionRule,
2895
+ "no-explicit-generics": noExplicitGenericsRule,
2896
+ "no-exposed-subjects": noExposedSubjectsRule,
2897
+ "no-finnish": noFinnishRule,
2898
+ "no-ignored-error": noIgnoredErrorRule,
2899
+ "no-ignored-notifier": noIgnoredNotifierRule,
2900
+ "no-ignored-observable": noIgnoredObservableRule,
2901
+ "no-ignored-replay-buffer": noIgnoredReplayBufferRule,
2902
+ "no-ignored-subscribe": noIgnoredSubscribeRule,
2903
+ "no-ignored-subscription": noIgnoredSubscriptionRule,
2904
+ "no-ignored-takewhile-value": noIgnoredTakewhileValueRule,
2905
+ "no-implicit-any-catch": noImplicitAnyCatchRule,
2906
+ "no-index": noIndexRule,
2907
+ "no-internal": noInternalRule,
2908
+ "no-nested-subscribe": noNestedSubscribeRule,
2909
+ "no-redundant-notify": noRedundantNotifyRule,
2910
+ "no-sharereplay": noSharereplayRule,
2911
+ "no-subclass": noSubclassRule,
2912
+ "no-subject-unsubscribe": noSubjectUnsubscribeRule,
2913
+ "no-subject-value": noSubjectValueRule,
2914
+ "no-subscribe-handlers": noSubscribeHandlersRule,
2915
+ "no-tap": noTapRule,
2916
+ "no-topromise": noTopromiseRule,
2917
+ "no-unbound-methods": noUnboundMethodsRule,
2918
+ "no-unsafe-catch": noUnsafeCatchRule,
2919
+ "no-unsafe-first": noUnsafeFirstRule,
2920
+ "no-unsafe-subject-next": noUnsafeSubjectNext,
2921
+ "no-unsafe-switchmap": noUnsafeSwitchmapRule,
2922
+ "no-unsafe-takeuntil": noUnsafeTakeuntilRule,
2923
+ "prefer-observer": preferObserverRule,
2924
+ "suffix-subjects": suffixSubjectsRule,
2925
+ "throw-error": throwErrorRule
2926
+ }
2927
+ };
2928
+ const configs = {
2929
+ recommended: createRecommendedConfig(plugin)
2930
+ };
2931
+ const index = {
2932
+ ...plugin,
2933
+ configs
2934
+ };
2935
+
2936
+ module.exports = index;