携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情
前言
大家好,我是陈同学,一枚野生前端开发者,感谢各位的点赞、收藏、评论
在前端发展日益强大的今天,我们的前端生产项目逐渐变得强大,因此也需要大量的开发人力支持,但每一位同学的代码编写风格都不太一致,有的同学可能喜欢使用双空格缩进而有的却喜欢用四空格,有的同学习惯语句后加分号而有的同学不喜欢加分号。我们需要有一个机制用于统一同一个项目当中的代码风格与规范,对这块有了解的同学应该马上就能想到著名的Eslint
大家平时可能在项目当中已经使用过Eslint工具,当遇到一些团队内部特殊的格式规范需求,就需要开发Eslint插件,今天我们就来聊一聊如何开发Eslint插件以及编写简单的规则
本文阅读成本与收益如下:
阅读耗时:7mins
全文字数:7k+
预期效益
- 掌握 Eslint 插件编写方法
初始化环境
ESLint有一个非常轻量级的开发环境,可以快速轻松地更新代码
安装 nodejs
若开发环境尚未安装 nodejs 环境,请前往 nodejs.org/ 下载并安装最新的稳定版本
同时确保npm包管理工具可用
安装插件项目生成器
全局安装yo、generator-eslint两个包,安装过程中会新增操作系统的全局命令yo
npm install -g yo generator-eslint
创建插件项目
- 建立一个项目文件夹(如:eslint-plugin-0)
- 进入项目文件夹(cd eslint-plugin-0)
- 运行
yo生成项目结构
mkdir eslint-plugin-0
cd eslint-plugin-0
yo eslint:plugin
通过一系列的命令行问答交互后即可获得初始项目目录结构
|-- eslint-plugin-0
|-- .eslintrc.js
|-- .gitignore
|-- README.md // 当前第三方包使用说明
|-- package-lock.json
|-- package.json
|-- docs // 存放Eslint规则说明文档
| |-- rules
|-- lib
| |-- index.js // 插件入口文件
| |-- rules // 存放规则定义文件
|-- tests // 存放测试用例
|-- lib
|-- rules
生成规则模版文件
创建插件项目后,默认是只有简单的目录结构
需要通过生成器命令生成自定义规则模版文件(docs、lib、tests)
yo eslint:rule
|-- eslint-plugin-0
|-- .eslintrc.js
|-- .gitignore
|-- README.md // 当前第三方包使用说明
|-- package-lock.json
|-- package.json
|-- docs // 存放Eslint规则说明文档
| |-- rules
| |-- demo-1.md // demo-1的规则解释
|-- lib
| |-- index.js // 插件入口文件
| |-- rules // 存放规则定义文件
| |-- demo-1.js // 自定义规则文件 demo-1(这里编写我们的AST检查逻辑代码)
|-- tests // 存放测试用例
|-- lib
|-- rules
|-- demo-1.js // demo-1的规则测试
lib/rules目录: 源码文件 (for example, demo-1.js)tests/lib/rules目录: 测试文件 (for example, demo-1.js)docs/src/rules目录: Markdown 文档文件 (for example, demo-1.md)
以上便是我们开发前的环境初始化流程
Eslint规则定义源码文件格式
// lib/rules/demo-1.js
'use strict';
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'can not use var',
recommended: true,
url: 'https://eslint.org/docs/rules/no-var',
},
fixable: 'code',
schema: [], // no options
},
create: function (context) {
return {
// callback functions
};
},
};
meta(object): 包含规则的元数据
- type (string) :包含规则的类型, 对应的值只能是"problem", "suggestion", "layout":
- "problem": 该规则标识的代码要么会导致错误,要么会导致令人困惑的行为。开发人员应该优先考虑解决这个问题
- "suggestion": 该规则确定了一些可以以更好的方式完成的事情,但如果不更改代码,就不会发生错误
- "layout": 该规则主要关注空格、分号、逗号和括号,这些程序中决定代码外观而不是执行方式的所有部分。这些规则适用于AST中没有指定的代码部分
- docs (object) 文档是 Eslint 规则必需组成:
- description (string) 提供一个关于规则的简短描述
- recommended (boolean) 配置文件当中是否继承 "extends": "eslint:recommended" 属性
- url (string) 指定一个 URL 能够看到完整的规则文档 (enabling code editors to provide a helpful link on highlighted rule violations)
在自定义规则或插件中,您可以省略文档或包含其中所需的任何属性
- fixable(string): 要么是"code"要么是"whitespace",如果命令行上的
——fix选项自动修复规则报告的问题
重要:固定属性对于固定规则是强制性的。如果没有指定这个属性,当规则试图产生一个修复时,ESLint将抛出一个错误。如果规则不可修复,则省略 fixable 属性
- hasSuggestions(boolean): 指定规则是否可以返回建议(如果省略默认为 false)
重要:hasSuggestions 属性对于提供建议的规则是强制的。如果这个属性没有设置为 true,当规则试图产生一个建议时,ESLint 将抛出一个错误。如果规则不提供建议,则省略 hasSuggestions 属性
-
schema(array): 指定 ESLint 可以防止无效规则配置的选项
-
deprecated(boolean): 表示规则是否已被弃用。如果规则没有被弃用,可以省略
deprecated属性 -
replacedBy(array): 对于已弃用的规则,指定替换规则
create(function): 返回一个带有方法的对象,ESLint 在遍历 JavaScript 代码的抽象语法树(由 ESTree 定义的 AST)时调用这些方法来“访问”节点
AST 结构预览器:astexplorer.net/
带方法的对象中的 key 有两种:选择器(节点类型/字符串表达式)、事件名
-
如果一个键是一个
节点类型或选择器,ESLint 在向下遍历树时调用 visitor 函数((node)=>{}) -
如果一个键是
节点类型或选择器:exit, ESLint 在向上遍历树时调用 visitor 函数((node)=>{}) -
如果一个键是一个
事件名,ESLint 调用 handler 函数进行代码路径分析((codePath, node)=>{})
// 官网文档示例
function checkLastSegment(node) {
// report problem for function if last code path segment is reachable
}
module.exports = {
meta: {
// ...
},
create: function (context) {
// declare the state of the rule
return {
ReturnStatement: function (node) {
// at a ReturnStatement node while going down
},
// at a function expression node while going up:
'FunctionExpression:exit': checkLastSegment,
'ArrowFunctionExpression:exit': checkLastSegment,
onCodePathStart: function (codePath, node) {
// at the start of analyzing a code path
},
onCodePathEnd: function (codePath, node) {
// at the end of analyzing a code path
},
};
},
};
实现一个简单的规则
检测目标:不允许使用 var 定义变量
// lib/rules/demo-1.js
module.exports = {
meta: {
// ...
},
create: function (context) {
function reportIllegalUsege(node) {
context.report({
node: context.getSourceCode().getFirstToken(node),
loc: node.loc,
message:
'reportIllegalUsege: get a error',
});
}
// declare the state of the rule
return {
'VariableDeclaration[kind="var"]': function (node) {
reportIllegalUsege(node); // eslint命令后提示问题
},
};
},
};
可以看到create回调函数的返回的对象中存在一个key'VariableDeclaration[kind="var"]'
VariableDeclaration为AST-Tree中node节点的type[kind="var"]为node节点当中存在属性kind并且其value值为var
综上这里key选择器的意思是当AST遍历过程中匹配到type: VariableDeclaration的node节点且该节点包括值为var的kind属性,则执行回调函数reportIllegalUsege(node);
对AST结构不熟悉的同学可以通过使用AST预览工具将需要检测的代码片段AST-Tree可视化:astexplorer.net/
到这里我们就已经编写好了一个Eslint插件中的规则,接下来我们根据需要进行docs/rules/下的说明文件的补充
发布eslint-plugin
完成上述步骤后,我们就可以将编写好的Eslint插件发布为一个npm包
发布之前记得在package.json当中修改一下包的名称,如:eslint-plugin-novar
'eslint-plugin-${diyName}': eslint插件包推荐使用这种命名格式
执行发布命令
# 在项目根目录下执行
npm login
npm publish
发布完成后便可以在需要用到这个插件的项目中安装插件依赖
npm i eslint --save-dev
npm install eslint-plugin-novar --save-dev
将novar添加到项目根目录下.eslintrc配置文件当中(没有的话手动创建一个). 前提是发布的插件包必需以eslint-plugin-作为前缀:
{
"plugins": [
"novar"
]
}
执行Eslint检查
最后我们可以通过eslint命令执行代码检查
npx eslint --ext js,ts
若代码当中有使用var定义变量的行为,将会给出相应提示报错
讲到最后
本文仅以一个简单的Eslint规则定义来发布一个插件,意在使大家了解如何根据自身需要编写Eslint插件
谢谢大家,我们下节再见!!!
感谢各位看到这里,如果你觉得本节内容还不错的话,欢迎各位的点赞、收藏、评论,大家的支持是我做内容的最大动力
本文为作者原创,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利