ESLint 是什么
ESLint 是一种用于 ECMAScript/JavaScript 的检查工具,其目标是使代码更加一致并避免错误,有以下特性:
- 使用 Espress 作为解析器;
- 在代码中基于 AST 来执行操作
- 完全可插拔(每一条规则都是一个插件)
总结起来就是:ESLint 是一个基于 AST 来实现 ECMAScript/JavaScript 的检查工具,主要用来保证代码质量,且有非常多的可配置项。
ESLint 的黄金搭档 Prettier
在团队开发中,保证代码质量是必须的,但是代码的风格也需要统一,解决方案就是 Prettier。
Prettier 认为代码风格很重要,但是呢,你们也别定制了,用我就完事儿了,其他的不用管了。
哪些是代码风格:
因为 Prettier 和 ESLint 其实是存在一部分重叠的功能,因此必然存在冲突项,想要让 ESLint 和 Prettier 和谐的跑起来,就需要使用 eslint-config-prettier 插件,来解决搭档间的小冲突。
npm install --save-dev eslint-config-prettier
复制代码
{
"extends": [
"some-other-config-you-use",
"prettier" // 一定要将 prettier 声明在最后,才能有效的覆盖有冲突的配置项
]
}
复制代码
如果想让所有的报错信息都由 ESLint 来处理,那么可以使用 eslint-plugin-prettier 这个插件,将 prettier 当做 ESLint 的插件来使用。
怎么配置 ESLint
两种方式配置:
- 在文件中通过注释配置
- 通过特定的配置文件
- .eslintrc.* (js|json|YAML)格式的配置文件
package.json
中的eslintConfig
配置文件中典型的配置项:
- Environments 指定 js 运行的环境,以自动注入全局变量,比如 nodejs es5 es6
- Globals 指定自定义的全局变量
- Rules 所有的规则配置项
- Plugins 插件配置项目
微信小程序中使用的 .eslintrc.js
文件 DEMO :
module.exports = {
env: {
browser: true,
es6: true,
node: true,
},
extends: ['eslint:recommended'],
parserOptions: {
sourceType: 'module',
ecmaVersion: 12
},
rules: {
indent: ['error', 'tab', { SwitchCase: 1 }],
'linebreak-style': ['error', 'unix'],
quotes: ['error', 'single'],
semi: ['error', 'always'],
'object-shorthand': ['error', 'always'],
'one-var': [
2,
{
var: 'never',
let: 'never',
const: 'never',
},
],
'no-invalid-regexp': 'error'
},
globals: {
getApp: false,
wx: false,
Page: false,
getCurrentPages: false,
Component: false,
App: false,
Behavior: false,
requirePlugin: false,
},
};
复制代码
因为 ESLint 的可配置项非常多,最佳实践就是继承通用的校验规则,比如 "extends": ["eslint:recommended"]
,然后再进行团队定制。
这个地方需要知道的小知识:
.eslintrc.*
会从检查的文件路径开始逐级向上搜索配置文件,直到根目录或则配置中包含有root: true
才会停止搜索,途中所有的.eslintrc.*
文件配置会被合并,根据就近优先原则,对冲突配置进行覆盖。如果项目的
package.json
文件中指定了eslintConfig
项,那么这个配置会被生效于项目的所有子目录,根据就近优先原则对冲突配置进行覆盖。
怎么基于 ESLint 进行创作
官方为了简化开发操作,已经基于 yo 定制了脚手架 generator-eslint,因此我们只需要几行命令就可以轻松搭建好开发环境。
# 全局安装 yo
npm install -g yo
# 安装 eslint 官方脚手架
npm install -g generator-eslint
复制代码
创建插件
# 创建一个项目文件夹比如
mkdir eslint-wd
# 进入目录
cd eslint-wd
# 通过脚手架创建基础文件,根据引导选择包含 rules
yo eslint:plugin
# 最后会生成如下结构的目录
# .
# ├── README.md
# ├── lib
# │ ├── index.js
# │ ├── processors
# │ └── rules
# ├── package-lock.json
# ├── package.json
# └── tests
# └── lib
# ├── processors
# └── rules
复制代码
创建 rule
# 创建自定义 rule 我们依然使用脚手架,在 eslint-wd 目录中执行
# 我们创建一个文件中不允许使用 require 引入模块变量为例子
yo eslint:rule
# 根据引导完成配置
#? What is your name? amin
#? Where will this rule be published? ESLint Plugin
#? What is the rule ID? no-require
#? Type a short description of this rule: 禁止项目中使用 require
#? Type a short example of the code that will fail: const fs = require('fs');
# create docs/rules/no-require.md
# create lib/rules/no-require.js
# create tests/lib/rules/no-require.js
复制代码
根据脚手架引导之后,会生成三个文件:
- docs/rules/no-require.md 文档描述
- lib/rules/no-require.js 核心逻辑
- tests/lib/rules/no-require.js 单元测试
进入 lib/rules/no-require.js
文件中,主要有两个核心部分:
meta
元数据-
type
指定规则的类型"problem"
可能会导致错误或者异常行为"suggestion"
一些优化建议,不修改也不会导致错误"layout"
代码风格
-
doc
对规则的一些文字描述还有文档地址description
(string)category
(string)recommended
(boolean)"extends": "eslint:recommended"
是否启用该规则url
(string)
-
fixable
"code"
"whitespace"
-
schema
配置说明
-
create(context) {}
函数返回一个对象,对象中定义各种在AST
深度遍历中的回调函数,简单的遍历可以直接以语法树中的节点类型作为函数名即可,更多用法可查看文档。
下面我以创建一个 禁用 require()
的插件为例,讲解过程:
首先,到 语法树在线预览网站 查看 AST
const fs = require('fs');
const { exec } = require('child_process');
复制代码
将代码贴进去后,可以在右侧看到 AST
的结构:
可以看到,我们要识别出 require
,我们只需要关注 CallExpression
类型的节点,并且这个节点的 callee
属性是一个 Identifier
, 且 name="require"
,那么代码就很容易实现了:
/**
* @fileoverview 禁止项目中使用 require
* @author amin`
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/**
* @type {import('eslint').Rule.RuleModule}
*/
module.exports = {
meta: {
type: "suggestion", // `problem`, `suggestion`, or `layout`
docs: {
description: "禁止项目中使用 require",
category: "Fill me in",
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
messages: {
avoidRequire: 'avoid use {{name}}',
}
},
create(context) {
// variables should be defined here
//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
// any helper functions should go here or else delete this section
//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------
return {
// visitor functions for different types of nodes
CallExpression(node) {
const callee = node.callee;
if (callee && callee.type === 'Identifier' && callee.name === 'require') {
context.report({
node,
messageId: 'avoidRequire', // 对应 meta 中定义的 messages 部分
data: {
name: callee.name,
},
});
}
}
};
},
};
复制代码
完善 Jest
测试:
/**
* @fileoverview 禁止项目中使用 require
* @author fumin
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const rule = require("../../../lib/rules/no-require"),
RuleTester = require("eslint").RuleTester;
//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015, sourceType: 'module' } });
ruleTester.run("no-require", rule, {
valid: [
'import fs from "fs"',
'import { exec } from "child_process"'
],
invalid: [
{
code: "const fs = require('fs');",
// output: 'avoid use require',
errors: [{ messageId: 'avoidRequire' }],
},
],
});
复制代码
写完测试用例后,可以执行 npm run test
来校验我们写的功能是否正确,校验通过后,我们可以到实际项目中来验证。
在插件根目录,通过 npm link
命令,将插件软链到全局目录中
在需要使用项目中,执行 npm link eslint-plugin-wd
,项目的 node_modules
中会创建出该插件软链
在 .eslintrc.*
文件中引入插件,在规则中配置规则:
{
plugins: ['eslint-plugin-wd'],
rules: {
'eslint-plugin-wd/no-require': 'error',
},
}
复制代码
这样项目中就启用了,刚创建的规则。