0
点赞
收藏
分享

微信扫一扫

制定企业级代码规范与检查

开篇一张图

制定企业级代码规范与检查_官网


前言

如何作出项目的亮点?

  1. 项目中遇到了什么问题?
  2. 解决问题的过程并且如何思考?
  3. 思考之后通过什么方式解决
  4. 最后这一个任务你学到了什么,给团队带来了什么价值,解决了哪些痛点。

就从我的题目说起,本篇文章告诉你针对定制代码规范和检查这个小需求如何做出亮点?看完本文后回顾上面提到的 4 点,感觉下。

本文目标

目标不是一次全部定出来的,在实践和调研过程中会添加一些

  1. 去掉项目中原有的​​TSLint​​​ ,统一使用​​ESLint​​​,但是在​​ESLint​​​ 中加入​​TSLint​​ 检测插件
  2. ​Prettier​​​ 支持的格式化规则全部使用​​Prettier​​​,不提供的使用​​ESLint​​​,以免冲突(个人认为​​Prettier​​ 提供的格式化规则可以满足开发者)。
  3. 代码保存时,支持自动​​fix​​​,只对自己控制范围内的​​fix​​​,范围外的内容依靠开发者配置或​​vscode​​ 自动配置。
  4. 格式化和​​ESLint​​​ 纳入项目级​​git​​ 跟踪,所有开发者统一。
  5. 除了上面的规范与检查实现,了解一些原理,比如​​rules​​​ 原理?为什么​​Prettier​​​ 和​​ESLint​​​ 冲突?​​Prettier​​ 原理?

ESLint

​ESLint​​​ 的原理就是一款插件化的javascript代码静态检查工具,其核心是对代码解析得到的 ​​AST (Abstract Syntax Tree 抽象语法树​​​)进行模式匹配,定位不符合约定规范的代码。​​ESLint​​ 是完全插件化的。每一个规则都是一个插件并且可以在运行时添加更多的规则。

社区比较知名的代码规范

  • eslint-config-airbnb
  • eslint-config-standard
  • eslint-config-alloy

如果想降低配置成本,可以直接接入上面的开源配置方案,好多开发者是继承它们的规范,然后在原有基础进行部分修改。我们目前选择的方式不是继承,挑选出了一些适合我们的 ​​ESLint​​ 规则(因为是在原有代码重新建立规范,防止改动过大)。

ESLint 集成

​ESLint​​​ 使用并不复杂,简单说下 ​​ESLint​​ 的集成。

全局安装

yarn add eslint -D

初始化

eslint --init

这个时候在项目中会出现一个 ​​.eslintrc.js​​ 的文件。

eslint 自定义配置文件

module.exports = {
parser: {}, //定义ESLint的解析器
extends: [], // 定义文件继承的子规范
plugins: [], // 定义了该eslint文件所依赖的插件
env: {},
rules: {} // 规则
};
parser

定义 ​​parser​​​ 的解析器,我们常用的解析器应该是 ​​@typescript-eslint/parser​​。

env

通过 ​​env​​ 配置需要启动的环境

env: {
es6: true, // 支持新的 ES6 全局变量,同时自动启用 ES6 语法支持
node: true, // 启动 node 环境
mocha: true,
},
extend

​extend​​​ 提供的是 ​​eslint​​ 现有规则的一系列预设。


这里注意的是,“extends”除了可以引入推荐规则,还可以以文件形式引入其它的自定义规则,然后在这些自定义规则的基础上用rules去定义个别规则,从而覆盖掉”extends”中引入的规则。


{
"extends": [
"./node_modules/coding-standard/eslintDefaults.js",
// Override eslintDefaults.js
"./node_modules/coding-standard/.eslintrc-es6",
// Override .eslintrc-es6
"./node_modules/coding-standard/.eslintrc-jsx",
],
"rules": {
// Override any settings from the "parent" configuration
"eqeqeq": "warn"
}
}

除了在配置文件中指定规则外,还可以在代码中指定规则,代码文件内以注释配置的规则会覆盖配置文件里的规则,即优先级要更高。平时我们常用的就是 ​​eslint-disable-next-line​​。

忽略检查可以通过在项目目录下建立 ​​.eslintignore​​​ 文件,并在其中配置忽略掉对哪些文件的检查。需要注意的是,不管你有没有在 ​​.eslintignore​​​ 中进行配置,​​eslint​​​ 都会默认忽略掉对 ​​/node_modules/**​​​ 的检查。也可以在 ​​package.json​​​ 文件的 ​​eslintIgnore​​ 字段进行配置。

plugins

​plugin​​​ 则提供了除预设之外的自定义规则,当你在 ​​ESlint​​ 的规则里找不到合适的的时候就可以借用插件来实现了

module.exports = {
parser: '@typescript-eslint/parser', // 解析器
extends: [
"./.eslintRules.js",
'plugin:prettier/recommended',
"prettier",// 优先 prettier 中的样式规范
'prettier/@typescript-eslint',
], // 继承的规则
plugins: ['@typescript-eslint'], // 插件

ESLint 重要特性

rules

​rules​​ 对应的规则,小伙伴可以去官网查看。找到符合自己项目的规则。

ESLint 规则官网地址

注意:
  1. 在整理总结规则的时候有些是自动检测的规则,就可以不用总结进去了。
  2. ​ESLint​​ 规则的三种级别
  • "off"或者0,不启用这个规则
  • "warn"或者1,出现问题会有警告
  • "error"或者2,出现问题会报错
rules 工作原理`

首先来看看 ​​eslin​​​ t源码中关于 ​​rules​​​ 的编写。​​eslint​​​ 中的 ​​rules​​​源码存在于 ​​lib/rules​​​ 下。每一个 ​​rules​​​ 都是一个 ​​node​​​ 模块,用 ​​module.exports​​​ 导出一个 ​​meta​​​ 对象及一个​​create​​ 函数。

module.exports = {
meta: {
type: "suggestion",

docs: {
description: "disallow unnecessary semicolons",
category: "Possible Errors",
recommended: true,
url: "https://eslint.org/docs/rules/no-extra-semi"
},
fixable: "code",
schema: [] // no options
},
create: function(context) {
return {
// callback functions
};
}
};

​meta​​​ 代表了这条规则的 ​​元数据​​​,如这条规则的类别,文档,可接收的参数 ​​schema​​​ 等等。​​create​​​ 返回一个对象,其中定义了一些在 ​​AST​​​ 遍历访问到对应节点需要执行的方法等等。函数接受一个 ​​context​​​ 对象作为参数,里面包含了例如可以报告错误或者警告的 ​​context.report()​​​、可以获取源代码的 ​​context.getSourceCode()​​ 等方法,可以简化规则的编写。

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) {
// 在AST从上向下遍历到ReturnStatement node 时执行
},
// 在AST 从下向上遍历到 function expression node 时执行:
"FunctionExpression:exit": checkLastSegment,
"ArrowFunctionExpression:exit": checkLastSegment,
onCodePathStart: function (codePath, node) {
// 在分析代码路径开始时执行
},
onCodePathEnd: function(codePath, node) {
// 在分析代码路径结束时执行
}
};
}
};

遍历 ​​AST​​​ 的过程中会以“从上至下”再“从下至上”的顺序经过节点两次,​​selector​​​ 默认会在下行的过程中执行对应的访问函数,如果需要再上行的过程中执行,则需要添加​​:exit​​。

TSLint 迁移到 ESLint 集成

背景

在这里会有读者问有现成的 ​​TSLint​​​ 不用,为什么要迁移到 ​​ESLint​​ 中集成?

解答下:由于性能问题,​​TypeScript​​​ 官方决定全面采用 ​​ESLint​​​,甚至把仓库​​(Repository)​​​作为测试平台,而 ​​ESLint​​​ 的 ​​TypeScript​​ 解析器也成为独立项目,专注解决双方兼容性问题。

​JavaScript​​​ 代码检测工具 ​​ESLint​​​ 在 ​​TypeScript​​​ 团队发布全面采用 ​​ESLint​​​ 之后,发布​​typescript-eslint​​​ 项目,以集中解决​​TypeScript​​​ 与 ​​ESLint​​​ 兼容性问题。而 ​​ESLint​​​ 不再维护 ​​typescript-eslint-parser​​​,也不会在 ​​npm​​​ 上做任何发布。​​TypeScript​​​ 解析器转移至 ​​Github​​​ 的 ​​typescript-eslint/parser​​。

官方都放弃了我们也没必要太坚持,而且通过 ​​ESLint​​​ 加上 ​​ts​​ 插件都可以完成检查

集成过程

首先安装依赖:

yarn add @typescript-eslint/parser @typescript-eslint/eslint-plugin -D

这两个依赖分别是:

  • ​@typescript-eslint/parser​​​:​​ESLint​​​ 的解析器,用于解析​​typescript​​​,从而检查和规范​​Typescript​​ 代码。
  • ​@typescript-eslint/eslint-plugin​​​:这是一个​​ESLint​​​ 插件,包含了各类定义好的检测​​Typescript​​ 代码的规范。

安装好​​2​​​个依赖之后,修改之前创建的​​.eslintrc.js​​​文件,在该文件中加入 ​​TSLint​​ 配置。

module.exports = {

parser: '@typescript-eslint/parser', //定义ESLint的解析器
extends: ['plugin:@typescript-eslint/recommended'],//定义文件继承的子规范
plugins: ['@typescript-eslint'],//定义了该 eslint 文件所依赖的插件
env:{
browser: true,
node: true,
},
parserOptions: {
parser: '@typescript-eslint/parser', // 解析 .ts 文件
ecmaVersion: 2019,
sourceType: 'module',
ecmaFeatures: {
modules: true,
},
},
}
  • 在​​typescript​​​ 项目中必须执行解析器为​​@typescript-eslint/parser​​​,才能正确的检测和规范​​typescript​​ 代码
  • ​env​​​ 环境变量配置,形如​​console​​​ 属性只有在​​browser​​​ 环境下才会存在,如果没有设置支持​​browser​​​ ,那么可能报​​console is undefined​​ 的错误。
  • 上面的配置中​​extends​​​ 中定义了了文件继承的子规范,使用的​​typescript-eslint​​ 默认的推荐规范
  • ​parserOptions​​ 解析器相关条件配置。

使用自定义的 typescript 规范

上面 ​​extends​​​ 中 ​​plugin:@typescript-eslint/recommended​​​ 使用的是插件默认推荐的 ​​typescript​​ 规范。但是会不会有同学不想使用推荐的规范,制定自己或者在推荐的规范中进行一些修改(比如一些老项目,加入规范,改动大,可能暂时忽略某些规范)

使用方式:如果想使用推荐,然后在推荐的基础上进行规范修改,可以直接在​​.eslintrc.js​​ 文件中的rules对象中添加。

举个例子

rules:{
'@typescript-eslint/adjacent-overload-signatures': 2, // 要求成员重载是连续的
}

具体想修改那些自定义规范,可以去官网查看,这里给出官网地址。

  • TSLint rule 官网

Prettier


无法确定一个让所有人都满意的方案,就很难执行下去!


​Prettier​​ 中文的意思是漂亮的、美丽的,是一个流行的代码格式化的工具。

我们都知道 ​​ESLint​​ 本身就带有格式化检查的,我们为什么要是使用它?它有什么优点?使用它要注意那些问题?

优点

​Perriter​​ 官网列出几个特点:

  • An opinionated code formatter (译:固执己见的代码格式化程序)
  • Supports many languages(译:支持多种语言)
  • Integrates with most editors(译:与大多数编辑器集成)
  • Has few options(译:没有什么选择)

其中最核心的点是 ​​opinionated​​​ ,​​google​​​ 翻译过来是固执己见的,在 ​​Pertiter​​ 中,就是说:你必须认同我的观点,按照我说的做。否则你就别用我,硬着头皮用就会处处不爽!

要解决的问题

  1. 使用​​Prettier​​​ 如何避免与​​ESLint​​​ 和​​TSLint​​ 的格式化冲突?
  2. ​Prettier​​​ 中不提供的格式化规则,​​ESLint​​ 中提供的可以兼容一起使用吗?

带着两个问题继续往下看

集成

安装模块包

我们来看如何结合 ​​ESLint​​ 来使用。首先我们需要安装三个依赖:

yarn add prettier eslint-config-prettier eslint-plugin-prettier -D

对每个依赖进行说明:

  1. ​prettier​​​:​​Prettier​​插件的核心代码。
  2. ​eslint-config-prettier​​​:解决​​ESLint​​​中的样式规范和​​Prettier​​​中样式规范的冲突,以​​Prettier​​​ 的样式规范为准,使​​ESLint​​ 中的样式规范自动失效。
  3. ​eslint-plugin-prettier​​​:将​​prettier​​​ 作为​​ESLint​​ 规范来使用。

创建 .prettierrc 文件

在项目的根目录下创建 ​​.prettierrc.js​​ 文件

module.exports =  {
"printWidth": 120,
"semi": false,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": false,
"jsxBracketSameLine": true,
"arrowParens": "avoid",
"insertPragma": true,
"tabWidth": 4,
"useTabs": false
};

每个属性的含义可以去 ​​Prettier​​ 中查看。

修改 .eslintrc.js 文件,引入 Prettier

在 ​​extends​​ 中添加

extends:[ 
'./.eslintRules.js',
'plugin:prettier/recommended',
'prettier', // 优先 prettier 中的样式规范
'prettier/@typescript-eslint',
// 这里可以加一些prettier不支持,eslint支持的格式化规则,但是个人认为prettier的格式化规则够用了
],

关于 Prettier 配置时特殊说明(重点看下)

网上好多在 ​​ESLint​​​ 中加入的 ​​Prettier​​​的文章,但是很少有讲清楚的,好多就是把配置文件写一下,然后很多小伙伴配置时候发现 ​​Prettier​​​ 的格式化还是和 ​​ESLint​​​中的格式化冲突,​​ctrl+s​​ 保存的时候甚至出现来回切换格式的冲突,不知道小伙伴们遇到过这种情况没。

所以还是知道下原理,​​extends​​ 中为什么那么写,格式冲突和顺序有什么关系没?

  1. ​eslint-config-prettier​​​ 源码可以看出,它的代码很简单,它实际就是关闭了​​eslint​​ 的所有格式化规则。


源码地址:https://github.com/prettier/eslint-config-prettier/tree/master/bin


  1. 我们​​yarn add​​​ 插件的时候​​eslint-config-prettier​​​ 模块实际是为​​eslint-plugin-prettier​​​ 插件服务的,在​​eslint-plugin-prettier​​​ 的源码中调用了​​eslint-config-prettier​​ 中相关的配置,然后执行插件中的代码。
  2. 看​​eslint-config-prettier​​​ 中​​recommended​​​部分 的源码,源码中也有使用到​​eslint-config-prettier​​​(把已有格式化配置关掉),然后自己制定了基础的​​recommended​​​ 版本,讲到这应该明白为什么在​​eslint-plugin-prettier​​ 中有一段最重要的话,需要把它(eslint-config-prettier)放在所有格式化配置的后面。

前面的内容,通过这个插件对前面 ​​ESLint​​​ 的配置进行重置。如果想使用一些 ​​Prettier​​ 中不支持的格式化配置,我们把eslint中的格式化加在他们后面写了,也不会有冲突。

  1. ​prettier/@typescript-eslint​​​ 是用来忽略​​typescript​​ 中的格式化配置。

这里关于防止 ​​Prettier​​​ 和 ​​ESLint​​ 冲突,画了一张


另外 ​​eslint-plugin-prettier​​​ 和 ​​eslint-config-prettier​​ 的源码都不是很复杂,感兴趣的同学可以去看看,下面是源码地址:

  • eslint-plugin-prettier 官网
  • eslint-config-prettier 官网


Prettier 原理简单说明

不管你写的代码是个什么鬼样子,​​Prettier​​​ 会去掉你代码里的所有样式风格,然后用统一固定的格式重新输出。输出时基本上只考虑一个参数,就是 ​​line length​​。

例如你写的这行代码:

foo(arg1, arg2, arg3, arg4);

一行装得下这么多代码,所以就不需要改。

如果你写了下面代码:

foo(reallyLongArg(), omgSoManyParameters(),IShouldRefactorThis(), isThereSeriouslyAnotherOne());

太长了,​​Prettier​​ 就会重新改成这样输出:

foo(
reallyLongArg(),
omgSoManyParameters(),
IShouldRefactorThis(),
isThereSeriouslyAnotherOne()
);

咱们再仔细探究一下这个过程。不管你之前写的代码是什么样,首先必须符合语法规范。​​Prettier​​​ 先把你的代码转换成一种中间状态,叫 ​​AST(Abstract Syntax Tree)​​。

用 ​​Prettier​​ 提供的 Playground 更直观一些


上图左侧是手写代码,中间是 AST(去掉了任何代码风格),右侧是重新输出的结果。

Prettier 就是在这个 AST 上重新按照自己的风格输出代码。

这是 Prettier 也搞懂后的最终配置

module.exports = {
parser: '@typescript-eslint/parser', // 解析器
extends: [
"./.eslintRules.js",
'plugin:prettier/recommended',
"prettier",// 优先 prettier 中的样式规范
'prettier/@typescript-eslint',
], // 继承的规则
plugins: ['@typescript-eslint'], // 插件
env: {
es6: true,
node: true,
mocha: true,
},
parserOptions: {
parser: '@typescript-eslint/parser', // 解析 .ts 文件
ecmaVersion: 2019,
sourceType: 'module',
ecmaFeatures: {
modules: true,
},
},
rules: {
}, // 规则
};

注意 ​​eslintRules​​​ 是 ​​base​​ 规则,单独提了出来。

VSCode 自动 fix 配置

因为终极目标是我们在使用​​eslint​​​格式化并且检查我们自己编写的 ​​javascript​​​ 和 ​​typescript​​​。除了我们要求的代码,如果开发者添加别的代码也应该进行格式化,除非忽略的文件,开发者本地安装什么插件我们管不到,在用户级别配置中 ​​setting.json​​ 中

{
// 此模式不能使用skipFiles特性,暂时关闭,需要调试其他进程时请在本地打开
// "debug.node.autoAttach": "on",
"editor.formatOnSave": true,
"debug.openDebug": "openOnFirstSessionStart",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"[javascript]": {
"editor.formatOnSave": false
},
"[typescript]": {
"editor.formatOnSave": false
}
}

lint 校验代码与 与 --fix 参数设置

上面的配置都做完了,如果不是一个新项目是原有的老项目,可能需要做一些改动喽!我们先 Fix一下。

npm 脚本中需要有这样的配置
"scripts": {
"lint": "eslint src",
"lint:create": "eslint --init"
}

执行命令​​npx run lint​​会出现如下的错误:

1:7   error  'lint' is assigned a value but never used  no-unused-vars
1:14 error Strings must use doublequote quotes
1:22 error Missing semicolon semi

3 problems (3 errors, 0 warnings)
2 errors, 0 warnings potentially fixable with the `--fix` option.

这里报了三个错误,分别是:

  • ​index.js​​​ 第1行第7个字符,报错编码规则为​​no-unused-vars​​​:变量​​lint​​ 只定义了,但是未使用;
  • ​index.js​​​ 第1行第14个字符,报错编码规则为​​quotes​​:编码规范字符串只能使用双引号,这里却使用了单引号;
  • ​index.js​​​ 第1行第22个字符,报错编码规则为​​semi​​:编码规范每行代码结尾必须加分号,这里没有加分号。

设置 ​​--fix​​ 参数

说明:这里给 ​​"lint": "eslint src --fix"​​​, 加上 ​​--fix​​​ 参数,是 ​​ESLint​​ 提供的自动修复基础错误的功能。

此时运行 ​​npm run lint​​​ 会看到少了两条报错信息,并不是说编码规范变了,而是 Eslint 自动修复了基础错误,打开 index.js 文件,可看到字符串自动变成了双引号,并且代码末尾也加上了分号。可惜的是 ​​--fix​​​ 只能修复基础的不影响代码逻辑的错误,像 ​​no-unused-vars​​ 这种错误只能手动修改。

总结

本文主要对开篇那张图片中的本地代码检查部分进行了详细讲解,从实践到原理,另外小伙伴们也可以想下我开篇提到的如何做出亮点,希望有所帮助,最后快去制定一个属于自己项目的规范与检查吧!

如果开篇图中后面 ​​CI/CD​​​ 部分感兴趣的可以找我讨论,后面会单独写一篇 ​​CI/CD​​ 文章,不然篇幅太长了,欢迎在看转发。

参考文章

  • Prettier 看这一篇就行了
  • ​​ESLint 在中大型团队的应用实践​​
  • 使用 ESLint+Prettier 规范 React+Typescript 项目
  • eslint-plugin-prettier 官网
  • Using ESLint and Prettier in a TypeScript Project
  • 十分钟了解eslint配置 && 编写自定义eslint规则


制定企业级代码规范与检查_typescript_02​​文章就分享到这,欢迎关注“前端大神之路”​制定企业级代码规范与检查_typescript_02 

制定企业级代码规范与检查_解析器_04




举报

相关推荐

0 条评论