ESLint 插件规则编写的正确打开方式

2022-08-06 16:46:17 浏览数 (2)

ESLint 插件规则编写的正确打开方式

1. ESLint 安装和配置

ESLint 是⼀个开源的代码静态分析修复⼯具 cli,解析代码为 AST 使用的是 espree 解析器,该解析器最初是从经典的 esprima 解析器中 fork 出来的,但是现在基于另一个媲美 esprima 的新轮子 acorn,同时,@babel/parser 也是基于 acorn 解析器的。

首先安装 eslint:

代码语言:shell复制
$ npm init
$ npm i eslint -D # 安装eslint
$ npm create @eslint/config # 初始化eslint配置⽂件

⽣成的配置⽂件如下:

代码语言:javascript复制
module.exports = {
  // 当前可以使用哪个环境的全局变量
  env: {
    browser: true, // 浏览器环境 document
    es2021: true, // ECMAScript语法 Promise
    node: true, // node环境 require
  },
  // extends 和 plugins 的区别:extends = plugin   rule
  extends: [
    'eslint:recommended',
  ],
  parserOptions: {
    ecmaVersion: 'latest', // ⽀持最新语法
    sourceType: 'module', // ⽀持模块化
    ecmaFeatures: {
      'jsx': true, // 支持 jsx
    },
  },
  // plugins: ['@typescript-eslint'],
  rules: { // eslint规则覆盖
    'semi': ['error', 'always'], // 0 off 1 warn 2 error
    'quotes': ['error', 'double'],
  },
  globals: { // 配置全局变量
    custom: 'readonly', // readonly 、 writable
  },
};

可以配置 parser 字段来指定使⽤的解析器,如配置 @typescript-eslint/parser 解析器解析 ts 语法。

代码语言:shell复制
$ npm install @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest
typescript -D

修改配置文件:

代码语言:javascript复制
"extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
],
"parser": "@typescript-eslint/parser", // 内部用的是 espress

2. ESLint 核心 api

eslint 会将源码解析为 AST 语法树,遍历 AST 语法树,将节点拍平

代码语言:txt复制
ESLint 核⼼API
 - lintFiles 检验⽂件
 - lintText 校验⽂本
 - loadFormatter 加载formatter
 - calculateConfigForFile 通过⽂件获取配置
 - isPathIgnored 此路径是否是被忽略的
 - static outputFixes 输出修复的⽂件
 - static getErrorResults 获得错误结果

CLIEngine 脚⼿架核⼼
 - getRules 获取规则
 - resolveFileGlobPatterns 解析⽂件成glob模式
 - executeOnFiles 根据⽂件执⾏逻辑
 - executeOnText 根据⽂本执⾏逻辑
 - getConfigForFile 获取⽂件的配置
 - isPathIgnored 此路径是否是被忽略的
 - getFormatter 获取输出的格式
 - static getErrorResults 获取错误结果
 - static outputFixes 输出修复的结果

Linter 校验js⽂本
 - verifyAndFix 校验和修复
 - verify 校验⽅法
 - _verifyWithConfigArray 通过配置⽂件校验
 - _verifyWithoutProcessors 校验没有processors
 - _verifyWithProcessor 校验有processors

3. 从模板生成 cslint 插件和 no-var 规则

在 eslint 配置中 extends = plugin rule,插件开发分为插件和规则,eslint 官⽅提供了 Yeoman 脚⼿架来⽣成插件和规则模板

代码语言:shell复制
$ npm install yo generator-eslint -g
$ yo eslint:plugin # 插件模板初始化,插件ID填写 cslint
# create package.json
# create .eslintrc.js
# create lib/index.js
# create README.md

$ yo eslint:rule # 规则模版初始化
# create docs/rules/no-var.md
# create lib/rules/no-var.js
# create tests/lib/rules/no-var.js

生成的模板文件如下:

代码语言:javascript复制
//  /lib/index.js
const requireIndex = require('requireindex');
module.exports.rules = requireIndex(__dirname   '/rules');

//  /lib/rules/no-var.js
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
  meta: {
    type: null, // `problem`, `suggestion`, or `layout`
    docs: {
      description: 'no var expression',
      recommended: false,
      url: null, // URL to the documentation page for this rule
    },
    fixable: null, // Or `code` or `whitespace`
    schema: [], // Add a schema if the rule has options
  },
  create(context) {
    return {};
  },
};

对插件入口文件 /lib/index.js 添加插件配置

代码语言:javascript复制
const requireIndex = require('requireindex');
module.exports = {
  rules: requireIndex(__dirname   '/rules'),
  configs: {
    recommended: {
      plugins: ['cslint'],
      rules: {
        'cslint/no-var': ['error'],
      },
    },
  },
  processors: {
    '.vue': {
      preprocess(code) { // 提取.vue 文件中的js
        return [code];
      },
      postprocess(messages) {
        console.log(messages);
        return [];
      },
    },
  },
};

4. no-var 规则开发

开发 no-var 规则

代码语言:javascript复制
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
  meta: {
    type: 'problem', // `problem`, `suggestion`, or `layout`
    docs: {
      description: '代码中不能包含var关键字',
    },
    fixable: 'code', // Or `code` or `whitespace`
    schema: [], // Add a schema if the rule has options
    messages: {
      unexpectedVar: '不能用 {{type}} ',
    },
  },
  create(context) {
    // 用于修复代码
    const sourceCode = context.getSourceCode();
    return {
      VariableDeclaration(node) { // 拦截var声明
        if (node.kind == 'var') {
          context.report({ // 警告
            node,
            data: { type: 'var' },
            messageId: 'unexpectedVar', // 和上面的配置对应
            fix(fixer) {
              const varToken = sourceCode.getFirstToken(node, { filter: t => t.value === 'var' });
              // var 替换为 let
              return fixer.replaceText(varToken, 'let');
            }
          });
        }
      }
    };
  },
};

5. 编写自动测试用例

代码语言:javascript复制
const rule = require('./lib/rules/no-var'),
  RuleTester = require('eslint').RuleTester;
const ruleTester = new RuleTester({
  parserOptions: {
    ecmaVersion: 'latest',
  }
});
ruleTester.run('no-var', rule, {
  valid: [
    { code: 'let a = 1' },
  ],
  invalid: [
    {
      code: 'var a = 1',
      errors: [{ message: '禁用 var' }],
      output: 'let a = 1',
    },
  ],
});

6. 插件及规则应用

在新项目中应用插件和规则

代码语言:javascript复制
module.exports = {
  'env': {
    'browser': true,
    'es2021': true,
    'node': true,
  },
  'plugins': ['cslint'],
  'rules': {
    'cslint/no-var': ['error', 'always'],
  },
};

或使用 extends 直接应用全部规则

代码语言:javascript复制
module.exports = {
  'env': {
    'browser': true,
    'es2021': true,
    'node': true,
  },
  extends: ['plugin:zlint/recommended'],
};

0 人点赞