Custom rule for typescript-eslint

543 views Asked by At

Versions

package version
node v11.15.0
typescript ~3.1.1
@typescript-eslint/eslint-plugin 4.0.0
eslint 7.0.0
@typescript-eslint/parser 4.0.0

Description

I've got a task to figuring out following problem in project in Javascript and Typescript project: max-depth, max-lines-per-function, complexity

But, incase of Typescript project, there isn't any rule in typescript-eslint to support above problem. Hence, I thought of writing custom rule with help of ChatGPT, which is giving me below error

npx eslint . 

Oops! Something went wrong! :(

ESLint: 7.0.0

/project/max-nesting-depth.ts:1
(function (exports, require, module, __filename, __dirname) { import { ESLintUtils, TSESTree } from '@typescript-eslint/utils';
                                                                     ^

SyntaxError: Unexpected token {
    at new Script (vm.js:85:7)
    at NativeCompileCache._moduleCompile (/project/node_modules/v8-compile-cache/v8-compile-cache.js:240:18)
    at Module._compile (/project/node_modules/v8-compile-cache/v8-compile-cache.js:184:36)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:827:10)
    at Module.load (internal/modules/cjs/loader.js:685:32)
    at Function.Module._load (internal/modules/cjs/loader.js:620:12)
    at Module.require (internal/modules/cjs/loader.js:723:19)
    at require (/project/node_modules/v8-compile-cache/v8-compile-cache.js:159:20)
    at Object.<anonymous> (/project/.eslintrc.js:1:80)
    at Module._compile (/project/node_modules/v8-compile-cache/v8-compile-cache.js:192:30)

Below is my custom-rule file and eslintrc

max-nesting-depth.ts

import { ESLintUtils, TSESTree } from '@typescript-eslint/utils';

export const rule = ESLintUtils.RuleCreator.withoutDocs({
  create(context) {
    const [maxDepth = 3] = context.options;
  
    function checkBlockDepth(node: TSESTree.Node, depth: number) {
      if (depth > maxDepth) {
        context.report({
          node,
          messageId: 'exceedsMaxDepth',
        });
      }
    }
  
    function traverse(node: TSESTree.Node, depth: number) {
      if (node.type === 'BlockStatement') {
        checkBlockDepth(node, depth);
      }
  
      depth++;
      for (const child of context.getSourceCode().getFirstTokens(node)) {
        if (child.type === 'Punctuator' && child.value === '{') {
          traverse(child, depth);
        }
      }
    }
  
    return {
      Program(node) {
        traverse(node, 0);
      },
    };
  },
  meta: {
    type: 'suggestion',
    docs: {
      description: 'Enforce a maximum nesting depth for blocks in TypeScript code.',
      category: 'Best Practices',
      recommended: true,
    },
    schema: [
      {
        type: 'integer',
        minimum: 1,
      },
    ],
    messages: {
      exceedsMaxDepth: 'Exceeded maximum nesting depth of {{maxDepth}}.',
    },
  },
});

eslintrc.js

const maxDepth = require('./max-nesting-depth.ts');

module.exports = {
  extends: [
    'eslint:recommended', 
    'plugin:@typescript-eslint/recommended'
  ],
  parser: '@typescript-eslint/parser',
  plugins: [
    '@typescript-eslint'
  ],
  root: true,
  rules: {
    'max-nesting-depth': maxDepth,
  },
};

My expectation is to get a way to find following issues in my Typescript project max-depth, max-lines-per-function, complexity


Edit 24th October 23:

When I tried running specific rules of ESLint with below configuration, It's giving output of just Javascript file and not Typescript files, which has many function above 20 lines.

And, I had commented extends values in config file to only get output of specific rules.

module.exports = {
  extends: [
    // 'eslint:recommended', 
    // 'plugin:@typescript-eslint/recommended'
  ],
  parser: '@typescript-eslint/parser',
  plugins: [
    '@typescript-eslint'
  ],
  root: true,
  rules: {
    "max-lines-per-function": ["error", 20],
  },
};

Output:

npx eslint .
=============

WARNING: You are currently running a version of TypeScript which is not officially supported by @typescript-eslint/typescript-estree.

You may find that it works just fine, or you may not.

SUPPORTED TYPESCRIPT VERSIONS: >=3.3.1 <4.1.0

YOUR TYPESCRIPT VERSION: 3.1.6

Please only submit bug reports when using the officially supported version.

=============

/Users/maq5ud/Documents/sigtuple/mandara-ui/src/karma.conf.js
  4:18  error  Function has too many lines (41). Maximum allowed is 20  max-lines-per-function

✖ 1 problems (1 errors, 0 warnings)

2

There are 2 answers

0
Josh On

(Answering in the context of writing custom rules)

Have you tried reading through the custom rules docs on https://typescript-eslint.io/developers/custom-rules? Those explain how to build up a custom rule with typescript-eslint. Your code has at least two differences:

  • (small) using context.options instead of the second parameter to the create function.
  • (major) the traverse method hails from the time of TSLint, an old linter predating typescript-eslint that is deprecated now. ESLint rules generally use ESQuery selectors to target nodes.

I'd recommend reading through the source code for the core ESLint rules you linked. The linked ESLint docs include "Rule source" links at the bottom. max-depth.js is only about 150 lines, including blank lines and comments. Rules for TS code are very similar to rules for JS code, and would look approximately the same.

As a general methodology, I'd also recommend reading through the typescript-eslint docs (and any docs linked at the top of each typescript-eslint docs page) before trying to use ChatGPT to write code.


Oh, and - the Oops! Something went wrong! :( error is because:

  • You tried telling ESLint to run the .ts source file directly. TS files generally need to be transpiled to JS to be run in Node.js.
  • ESLint right now (version 8) doesn't support ECMAScript Modules (at least when run in a CommonJS project). You'll need to have your files be transpiled to CommonJS. See the TypeScript Handbook modules reference.
4
Josh On

(Answering in the context of what you're trying to do)

Have you tried using the core ESLint complexity, max-depth, and max-lines-per-function rules on TypeScript files? It doesn't look like there's anything in their source code that would break on TypeScript syntax.

typescript-eslint will generally add ["extension" rules](See https://typescript-eslint.io/rules/#extension-rules) around ESLint core ones only when the core ones break on TypeScript syntax. For example, @typescript-eslint/class-methods-use-this adds support for TypeScript's override keyword). If there's nothing broken about the core rule, there's no need to override it. For example, no-useless-rename and object-shorthand generally work fine (as of October 2023) in TypeScript code (I use them in create-typescript-app directly).

Request: please do use the ESLint core rules themselves. If there are any bugs, please search for / file them on https://github.com/typescript-eslint/typescript-eslint.