cartant / eslint-plugin-rxjs

ESLint rules for RxJS
MIT License
312 stars 37 forks source link

rxjs/no-exposed-subjects rule doesn't work properly #91

Closed ko1ebayev closed 1 year ago

ko1ebayev commented 2 years ago

Problem

Hello! Facing a strange behavior of no-exposed-subjects rule, as I recently read in docs "Disallows exposed subjects. In classes, Subject properties and methods that return a Subject must be private.", so I guess eslint must notify me about three errors if I will write this:

@Component({
  selector: 'app-component',
  ....
})
export class AppComponent {
   public foo$: Subject<unknown>;

   public bar$ = new Subject<unknown>();

   public getFoo(): Subject<unknown> {
     return this.foo$;
   }
}

But it marks as error only public "getFoo" method that returns public Subject. I expected public bar$ = new Subject<unknown>(); and public foo$: Subject<unknown>; are wrong too. Can someone explain this situation? Thanks a lot!

.eslintrc.js config

module.exports = {
  root: true,
  overrides: [
    {
      files: ['*.ts'],
      plugins: ['import', 'unused-imports', 'angular-rubic', 'rxjs', 'rxjs-angular'],
      parserOptions: {
        project: ['tsconfig.*?.json', 'e2e/tsconfig.e2e.json'],
        tsconfigRootDir: __dirname,
        createDefaultProgram: true
      },
      extends: [
        'plugin:@angular-eslint/recommended',
        'airbnb-typescript/base',
        'plugin:prettier/recommended',
        'plugin:rxjs/recommended'
      ],
      rules: {
        'import/prefer-default-export': 'off',
        '@typescript-eslint/no-useless-constructor': 'off',
        'no-plusplus': 'off',
        'class-method-use-this': 'off',
        'no-underscore-dangle': 'off',
        'no-inferrable-types': 'off',
        '@typescript-eslint/no-explicit-any': 2,
        '@typescript-eslint/no-unused-vars': 'off',
        'unused-imports/no-unused-imports': 'error',
        'unused-imports/no-unused-vars': [
          'error',
          {
            vars: 'all',
            args: 'all',
            ignoreRestSiblings: false,
            argsIgnorePattern: '^_'
          }
        ],
        '@angular-eslint/no-output-on-prefix': 'off',
        '@typescript-eslint/no-inferrable-types': 'off',
        '@angular-eslint/no-input-rename': 'off',
        'class-methods-use-this': 'off',
        complexity: ['error', 20],
        eqeqeq: ['error', 'always', { null: 'ignore' }],
        'no-magic-numbers': 'off',
        '@typescript-eslint/naming-convention': [
          'error',
          {
            selector: 'enumMember',
            format: ['UPPER_CASE']
          }
        ],
        // Styling.
        'array-bracket-spacing': ['error', 'never'],
        'object-curly-spacing': ['error', 'always'],
        // Temporary rules. Remove after full refactoring.
        'import/no-extraneous-dependencies': 'off',
        '@typescript-eslint/dot-notation': 'off',
        'no-restricted-globals': 'off',
        '@typescript-eslint/no-empty-function': 'off',
        'no-param-reassign': 'off',
        // Temporary rules. Remove as fast as it can be.
        'max-classes-per-file': 'off',
        radix: ['warn', 'as-needed'],
        'no-prototype-builtins': 'off',
        'no-return-assign': 'off',
        'no-restricted-syntax': ['error', 'LabeledStatement', 'WithStatement'],
        'no-console': [
          'warn',
          {
            allow: ['debug', 'error', 'info', 'warn']
          }
        ],
        'no-empty': ['error', { allowEmptyCatch: true }],
        '@typescript-eslint/return-await': 'off',
        'angular-rubic/explicit-function-return-type': 2,
        'no-continue': 'off',
        /* RxJs */
        'rxjs/finnish': [
          'error',
          {
            functions: false,
            methods: false,
            names: {
              '^(canActivate|canActivateChild|canDeactivate|canLoad|intercept|resolve|validate)$': false
            },
            parameters: true,
            properties: true,
            strict: false,
            types: {
              '^EventEmitter$': false,
              '^TuiDialogService$': false
            },
            variables: true
          }
        ],
        'rxjs/no-exposed-subjects': 'error',
        'rxjs-angular/prefer-async-pipe': 'warn',
        'rxjs-angular/prefer-takeuntil': [
          'warn',
          {
            checkComplete: false,
            checkDecorators: ['Component'],
            checkDestroy: false
          }
        ]
      }
    },
    {
      files: ['*.component.html'],
      extends: ['plugin:@angular-eslint/template/recommended', 'plugin:prettier/recommended'],
      rules: {
        'max-len': ['warn', { code: 200 }]
      }
    },
    {
      files: ['*.component.ts'],
      extends: ['plugin:@angular-eslint/template/process-inline-templates']
    },
    {
      files: ['src/**/*.spec.ts', 'src/**/*.d.ts'],
      parserOptions: {
        project: './src/tsconfig.spec.json'
      },
      extends: ['plugin:jasmine/recommended'],
      plugins: ['jasmine'],
      env: { jasmine: true },
      rules: {
        '@typescript-eslint/no-unused-vars': 'off'
      }
    }
  ]
};

Versions of packages:

"eslint": "^7.32.0",
"eslint-plugin-rxjs": "^3.3.7",
"eslint-plugin-rxjs-angular": "^1.0.6"

Please reply if you need some more info to investigate!

cartant commented 2 years ago

You'd need to provide a minimal reproduction of the issue, as this test suggest that the rule behaves correctly:

https://github.com/cartant/eslint-plugin-rxjs/blob/8332e6c210c3beac46501cd5b8c72fbd86d7eb2f/tests/rules/no-exposed-subjects.ts#L205-L217