TypeScript手写一个简单的eslint插件实例

 更新时间:2023年02月06日 09:50:59   作者:hans774882968  
这篇文章主要为大家介绍了TypeScript手写一个简单的eslint插件实例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

看到参考链接1以后,觉得用TS写一个eslint插件应该很简单🐔⌨️🍚,尝试下来确实如此。

前置知识

本文假设

  • 你对AST遍历有所了解。
  • 你写过单测用例。

第一个eslint规则:no-console

为了简单,我们只使用tsc进行构建。首先package.json需要设置入口"main": "dist/index.js",tsconfig.json需要设置"outDir": "dist""include": ["src"]。接下来设计一下单元测试和构建命令:

"scripts": {
  "clean": "rm -Rf ./dist/",
  "build": "yarn clean && mkdir ./dist && tsc",
  "test": "jest",
  "test:help": "jest --help",
  "lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\" \"test/**/*.{js,jsx,ts,tsx}\" \"*.{js,jsx,ts,tsx}\" --fix"
},

ESLintUtils.RuleTester写的.test.tsmocha或者jest都能运行,我选择了jest

当我们运行yarn lint时,node_modules/@eslint/eslintrc/lib/config-array-factory.jsthis._loadPlugin会加载插件,相当于在node环境运行上面指定的入口点dist/index.js。所以我们需要知道@eslint如何描述一个eslint插件,才能写出src/index.ts。查看this._loadPlugin可知,plugin.definition的类型应为:

type Plugin = {
  configs?: Record<string, ConfigData> | undefined;
  environments?: Record<string, Environment> | undefined;
  processors?: Record<string, Processor> | undefined;
  rules?: Record<...> | undefined;
}

结合参考链接1,我们得出结论:一般来说需要提供rulesconfigs属性。rules可以理解为具体的规则定义;configs可以理解为规则的集合,可以称为“最佳实践”,最常见的configsrecommended

于是写出src/index.ts

import rules from './rules';
import configs from './configs';
const configuration = {
  rules,
  configs
};
export = configuration;

src/rules/index.ts

import noConsole from './noConsole';
const allRules = {
  'no-console': noConsole
};
export default allRules;

src/configs/index.ts

import all from './all';
import recommended from './recommended';
const allConfigs = {
  all,
  recommended
};
export default allConfigs;

src/configs/all.ts

export default {
  parser: '@typescript-eslint/parser',
  parserOptions: { sourceType: 'module' },
  rules: {
    '@hans/use-i18n-hans/no-console': 'error'
  }
};

我们用createRule函数来创建一条规则。它需要传一个对象,我列举一下这个对象常用的几个属性:

  • meta.schema:配置eslint规则的时候可以指定的options参数。通常传入的值为{}(不接收参数)和object[]
  • meta.messages:一个对象,{ messageId: text }
  • create方法:eslint需要建立AST并遍历,所以要拿到这个方法的返回值作为遍历AST的配置。输入参数是context对象,常用的方法有:context.options[0]获取传入的参数;context.getFilename()获取当前yarn lint正在解析的文件名;context.report函数向用户报错,通常这么用:context.report({ node, messageId: 'xxMessageId' })messageId必须符合meta.messages给出的定义。create方法返回的对象有点类似于@babel/traversetraverse方法的第二个参数,具体写法看参考链接1的项目就行。
import { TSESLint, ASTUtils } from '@typescript-eslint/utils';
import createRule from '../utils/createRule';
import path from 'path';
import multimatch from 'multimatch';
// 模仿babel中的写法 import { isIdentifier } from '@babel/types';
const {
  isIdentifier
} = ASTUtils;
const whiteList = ['memory'];
const rule = createRule({
  name: 'no-console',
  meta: {
    docs: {
      description: 'Remember to delete console.method()',
      recommended: 'error',
      requiresTypeChecking: false
    },
    messages: {
      rememberToDelete: 'Remember to delete console.method()'
    },
    type: 'problem',
    schema: {}
  },
  create (
    context: Readonly<TSESLint.RuleContext<'rememberToDelete', never[]>>
  ) {
    return {
      MemberExpression (node) {
        if (isIdentifier(node.object) && node.object.name === 'console' &&
            isIdentifier(node.property) && Object.prototype.hasOwnProperty.call(console, node.property.name) &&
            !whiteList.includes(node.property.name)
        ) {
          context.report({ node, messageId: 'rememberToDelete' });
        }
      }
    };
  }
});
export default rule;

代码传送门:src/rules/noConsole.ts

本地测试

单元测试:

yarn test

本地查看效果

首先:

yarn build

在另一个项目(这里用了相对路径,用绝对路径也行):

yarn add -D file:../eslint-plugin-use-i18n-hans

注意:每次重新build后都需要在另一个项目重新yarn add

这样会得到:

{
  "devDependencies": {
    "@hans/eslint-plugin-use-i18n-hans": "file:../eslint-plugin-use-i18n-hans",
  }
}

接下来配置.eslintrc.js

module.exports = {
  plugins: [
    '@hans/use-i18n-hans',
  ],
  extends: [
    'plugin:@hans/use-i18n-hans/recommended',
  ],
}

插件名为@hans/use-i18n-hans,使用了configs中的recommended

最后重启vscode或运行yarn lint就能看到我们的第一个eslint插件生效了。

<path>/file-encrypt/webpack-plugin-utils.js
  16:5  error  Remember to delete console.log()  @hans/use-i18n-hans/no-console

no-console规则添加功能:排除用户指定的文件

修改一下meta.schema,新增输入参数:

schema = [
  {
    properties: {
      excludedFiles: {
        type: 'array'
      }
    }
  }
]

和对应的类型定义:

type Options = [{
  excludedFiles: string[];
}];
{
  create (
    context: Readonly<TSESLint.RuleContext<'rememberToDelete', Options>>
  ) {}
}

然后在create函数体加几句判定:

const fileName = context.getFilename();
const options = context.options[0] || {};
const { excludedFiles } = options;
if (Array.isArray(excludedFiles)) {
  const excludedFilePaths = excludedFiles.map(excludedFile => path.resolve(excludedFile));
  if (multimatch([fileName], excludedFilePaths).length > 0) {
    return {};
  }
}

context.getFilename()文档:eslint.org/docs/latest… 。其特性:在yarn test时会返回file.ts,在作为npm包引入另一个项目后,可以正常获取文件的绝对路径。

为了支持glob语法,我们引入了multimatch。但需要指定版本为5.0.0,因为multimatch6.0.0只支持es module,而我反复尝试都无法找到一个可以生效的jest配置(transformIgnorePatterns等配置项的资料都极少,这篇blog看上去操作性很强,但尝试后依旧无效……)。

构建完成后,我们可以在另一个项目尝试配置@hans/use-i18n-hans/no-console规则:

'@hans/use-i18n-hans/no-console': ['error', {
  excludedFiles: [
    'add-copyright-plugin.js',
    'copyright-print.js',
    'webpack-plugin-utils.js',
    'src/utils/my-eslint-plugin-tests/no-warn-folder/**/*.js',
    'tests/**/*.js',
  ],
}],

.eslintrc.js取消或添加注释并保存,vscode应该能立刻看到报错的产生和消失。

TODO:是否能够mock context.getFilename(),让本地可以写测试用例?

发布npm包

TODO。实用的eslint规则写好后将更新文章~

参考资料

以上就是TypeScript手写一个简单的eslint插件实例的详细内容,更多关于TypeScript eslint插件的资料请关注脚本之家其它相关文章!

相关文章

  • 数据结构TypeScript之二叉查找树实现详解

    数据结构TypeScript之二叉查找树实现详解

    这篇文章主要为大家介绍了数据结构TypeScript之二叉查找树实现详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • type challenge刷题之(middle 部分)示例解析

    type challenge刷题之(middle 部分)示例解析

    这篇文章主要为大家介绍了type challenge刷题之(middle 部分)示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • TypeScript逆变之条件推断和泛型的应用示例详解

    TypeScript逆变之条件推断和泛型的应用示例详解

    这篇文章主要为大家介绍了TypeScript逆变之条件推断和泛型的应用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • typescript type 分配条件类型示例详解

    typescript type 分配条件类型示例详解

    这篇文章主要为大家介绍了typescript type 分配条件类型示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • TypeScript数据结构链表结构 LinkedList教程及面试

    TypeScript数据结构链表结构 LinkedList教程及面试

    这篇文章主要为大家介绍了TypeScript数据结构链表结构 LinkedList教程及面试,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • 详解什么是TypeScript里的Constructor signature

    详解什么是TypeScript里的Constructor signature

    这篇文章主要介绍了什么是TypeScript里的Constructor signature详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07
  • TypeScript Module Resolution解析过程

    TypeScript Module Resolution解析过程

    这篇文章主要为大家介绍了TypeScript Module Resolution解析过程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07
  • TypeScript类型any never void和unknown使用场景区别

    TypeScript类型any never void和unknown使用场景区别

    这篇文章主要为大家介绍了TypeScript类型any never void和unknown使用场景区别,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • postman数据加解密实现APP登入接口模拟请求

    postman数据加解密实现APP登入接口模拟请求

    对于Postman的使用,一般情况下只要发发确定的请求与参数就可以的了,然而,在使用的时候,尤其是接口测试时,请求接口的设计里面都有数据加密,参数验签,返回数据也有进行加密的,这个时候就需要使用一些脚本做处理,模拟app登入请求的操作
    2021-08-08
  • TypeScript保姆级基础教程

    TypeScript保姆级基础教程

    这篇文章主要为大家介绍了TypeScript保姆级基础教程示例详解,主要为大家介绍了typescript的类型,函数,对象,接口等基础示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2022-07-07

最新评论