相信每个团队都有一套代码规范,但遵循规范全靠自觉,等codereveiw再指出就浪费团队精力了,最好的就是在开发时就能有提醒,这就需要eslint出场了。虽然eslint已经提供了丰富的内置规则和插件生态,但有时还是需要开发自己团队的规则集。让我们来开发一个微信小程序相关的。
eslint-plugin-ahaha-miniprogram
插件名就是这个,一个插件包含一组规则,这次开发这样一个规则:no-native-api,帮助我们禁用一些原生api, 比如不能使用同步api, 如果统一使用miniprogram-api-promise,那么需要禁止使用wx.xxx
规则分析
禁用什么api需要能配置,而且需要能定制提示信息,比如提示disallow use of native api: wx.navigateTo, use wxp.navigateTo instead, 如果不提供,默认:disallow use of native api: wx.navigateTo。所以最终的配置格式是这样:
{
"rules": {
"ahaha-miniprogram/no-native-api": [
"error",
{
"getStorageSync": true, // equal 'disallow use of native api: wx.getStorageSync'
"navigateTo": "disallow use of native api: wx.navigateTo, use wxp.navigateTo instead" // custom message
}
]
}
}
启动项目
官方已经提供了脚手架,直接安装:
$ npm install -g yo
$ npm install -g generator-eslint
$ mkdir eslint-plugin-ahaha-miniprogram && cd eslint-plugin-ahaha-miniprogram
$ yo eslint
然后按提示填写即可
需要其他工程配置的可以使用:generator-libs
开发规则
在lib/rules下创建no-native-api.js
描述规则
按我们定义的规则配置加入以下描述性内容:
module.exports = {
meta: {
type: 'problem', // problem | suggestion | layout
docs: {
description: 'Disallow Use of some native api: wx.xxx',
category: 'Best Practices',
recommended: true,
},
schema: [ // eslint使用jsonschema:https://json-schema.org/understanding-json-schema 做配置校验
{
type: 'object',
minProperties: 1,
additionalProperties: {
oneOf: [
{ type: 'string', minLength: 1 }, // 如果是string类型是自定义提示信息
{ type: 'boolean', enum: [true] }, // 如果是boolean类型则是是否检测该api
],
},
},
],
messages: { forbid: 'disallow use of native api: wx.{{api}}' }, // 消息定义,方便单元测试和代码内复用,不然会重复
},
};
实现规则
规则的实现使用create(context){},返回的是一个遍历ast节点的对象,和babel插件的visit一样。
先来验证参数,未配置则跳过:
const forbiddenApis = context.options[0] || {};
if (!Object.keys(forbiddenApis).length) {
return {};
}
然后找目标ast节点,wx.xxx是MemberExpression类型(使用ast神奇astexplorer查看):
return {
MemberExpression(node) {},
};
继续判断是不是目标ast节点:
if (node.object.name !== 'wx') {
// 不是wx.xxx
return;
}
然后根据我们的配置格式,如果api无限制,则跳过:
const message = forbiddenApis[node.property.name];
if (!message) {
return;
}
最后就是使用了限制的api,则要报错:
// https://cn.eslint.org/docs/developer-guide/working-with-rules#contextreport
context.report({
node,
message: message === true ? undefined : message, // 如果是自定义提示信息则使用 message 字段
messageId: message === true ? 'forbid' : undefined, // 如果是默认提示信息则使用 messageId 字段,值是描述规则的 messages 里的一项
// messages 可以使用模板语法,这是传递的数据
data: {
api: node.property.name,
},
});
完整代码:
create(context) {
const forbiddenApis = context.options[0] || {};
if (!Object.keys(forbiddenApis).length) {
return {};
}
return {
MemberExpression(node) {
if (node.object.name !== 'wx') {
return;
}
const message = forbiddenApis[node.property.name];
if (!message) {
return;
}
context.report({
node,
message: message === true ? undefined : message,
messageId: message === true ? 'forbid' : undefined,
data: {
api: node.property.name,
},
});
},
};
},
单元测试
好了,代码撸完了,要看看是否可行了,加个单元测试看看,eslint已经提供了单元测试功能,十分省事。
新建tests/lib/rules/no-native-api.js,加入以下代码:
const { RuleTester } = require('eslint');
const rule = require('../../../lib/rules/no-native-api');
const ruleTester = new RuleTester({
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
});
ruleTester.run('no-native-api', rule, {
// 什么样的是对的
valid: [
{
code: `wx.getStorageSync()`,
options: [{ navigateBack: true }],
},
// 其他
],
// 什么样的是错误的
invalid: [
{
code: `wx.getStorageSync()`,
options: [{ getStorageSync: true }],
errors: [{ messageId: 'forbid' }],
},
// 其他
],
});
然后 npm test 即可
发布
如果要使用需要发布到npm,然后在项目中安装。使用npm publish发布即可,发布前要检查包名是否已被占用,否则会报错。
总结
到这里一个基本的eslint插件就完成了,可以根据团队代码规范逐步添加规则,完整项目请查看:eslint-plugin-ahaha-miniprogram。
人总是不靠谱的,交给计算机才是王道,有了自己的eslint插件那治理团队代码就犹如烹小鲜了,嗷呜。