给你的代码加点使用权限(eslint)

391 阅读3分钟

背景

今天有遇到一个问题,合作开发的项目,某个非公用方法被其他同事引用导致的bug。之前也有遇到过类似的问题,当项目越来越庞大时候,我们需要限制管理某些模块/方法是否可被哪些模块调用;
原本考虑的方案是通过打包工具插入路径标识和配置处理,但后来一想都觉得不太合理(拓展性不强/代码侵入性过大等)。直接通过eslint进行代码提示会比较好,不会侵入代码,而且目前eslint已经更像是一个语法检查的平台了,集成了tslint, prettier等,也有很多基于eslint开发的插件。
源码仓库地址

如何调试eslint plugin

调试都是开发的重中之重,目前eslint提供的测试方式是提供了一些rule运行的api,可以在test目录下去执行引入rule。然后可以通过vscode debug(generator-eslint是通过mocha测试框架来测试的)实现断点调试;

// launch.json 配置
{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "mocha",
      "type": "node",
      "request": "launch",
      "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
      "stopOnEntry": false,
      "args": ["tests", "--recursive"], //you can specify paths to specific tests here
      "cwd": "${workspaceRoot}",
      "runtimeExecutable": null,
      "env": {
        "NODE_ENV": "testing"
      }
    }
  ]
}

具体可在源码仓库中查看

开发流程

1 通过generator-eslint插件初始化eslint-plugin项目; 2 创建rules;

// cd lib/rules && touch import.js
module.exports = {
  create: function(context) {
  }
}

3 通过eslint提供的 ImportDeclaration hook对ImportDeclaration进行处理;

// import.js
module.exports = {
  create: function(context) {
    return {
      ImportDeclaration(node) {
        // do something
      }
    }
  }
}
4 填写测试用例;  
5 npm publish;

难点

1 如何判断节点是ImportDeclaration,获取import声明中的source内容:
eslint中rules的create方法中直接提供了ImportDeclaration方法,并在ImportDeclaration调用时候提供了node参数,可以直接调用,e.g.

create: function(context) {
  return {
    ImportDeclaration(node) {
      // source
      let source = node.source.value // e.g. import { vb } from './../utils'
      console.log(source) // './../utils'
    }
  }
}

2 ImportDeclaration里面的路径形式多种(alias, relative path, absolute path),如何判断路径属于我们的路径;

问题记录

一、plugin开发中有两种方式,Processors和rules。Processors主要做文件处理,提供了三个api:

1 preprocess,将文件内容和文件名称作为参数,返回一个要检查的字符串数组。
2 postprocess, 方法接受一个二维数组,包含检测消息和文件名。
3 supportsAutofix,是否自动修复。

Processors用的比较少,一般情况下文件处理这些我们会通过打包工具等进行操作。
本文中主要是使用rules,提供了两个api:

1 meta: 定义规则的信息,如规则类型type, 提示文案,规则跳转url等;  
2 create: create提供了一个context对象,context对象提供了大量的ast相关的方法,如获取共享配置项,文件名,源码等;

二、如何获取当前rule的配置项;
有两种方式:

// 1 通过公用的 shared settings 获取
// 对于测试用例,在实例化测试对象时,配置settings即可
new RuleTester({
  settings
})
// 对于实际插件中eslint配置settings选项即可
// 2 通过context.options获取
// 对于测试用例,需要在parse时传options选项
valid: [
    {
        code: `import { vb } from './../utils'`,
        options
    }
]
// 对于实际插件中eslint配置,需要通过name和key,例如我这个项目
rules: {
  'authority-import/import': options
}

通过rules配置,只能设置可枚举的几个变量"off"|"warn"|"error"。但当前这个插件需要自定义配置,所以需要用settings。

三、插件开发完成后,必须在rules中进行配置,才能生效;

四、eslint是如何渲染错误提示内容的;
是vscode插件提供的api,具体可以参考这个sample

JSON Schema

JSON Schema是当eslint需要自定义配置项时候用来自定义配置的数据模型,用于描述json数据结构的规范(相当于json的类型定义);

TODO

1 缩小权限粒度,限制模块里某些方法的调用权限;
2 支持vue/react模块;
3 测试用例覆盖率;

参考文档

1 Working with Rules: eslint.org/docs/develo…
2 generator-eslint: github.com/eslint/gene…
3 手摸手教你写个ESLint插件以及了解ESLint的运行原理: obkoro1.com/web_accumul…
4 eslint-plugin-import: github.com/import-js/e…
5 Eslint 的实现原理,其实挺简单: juejin.cn/post/702525…