> eslint src
C:\Code\Git\algorithms\src\greet.ts
2:16 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types
✖ 1 problem (0 errors, 1 warning)
此时可以发现之前执行 lint 命令的错误通过插件的形式可实时在 VS Code 编辑器中进行显示。除此之外,一些 ESLint 的格式校验错误(例如多余的; 等)可通过配置 Save Auto Fix 进行保存自动格式化处理。具体 VS Code 的配置可参考 ESLint 插件 的文档说明,这边应该需要进行如下配置:
PS C:\Code\Git\algorithms> npx eslint --print-config src/index.ts | npx eslint-config-prettier-check
No rules that are unnecessary or conflict with Prettier were found.
例如把 eslint-config-prettier 的配置去除,此时进行检查重复规则:
PS C:\Code\Git\algorithms> npx eslint --print-config src/index.ts | npx eslint-config-prettier-check
The following rules are unnecessary or might conflict with Prettier:
- @typescript-eslint/no-extra-semi
- no-mixed-spaces-and-tabs
The following rules are enabled but cannot be automatically checked. See:
https://github.com/prettier/eslint-config-prettier#special-rules
- no-unexpected-multiline
husky > pre-commit (node v12.13.1)
[STARTED] Preparing...
[SUCCESS] Preparing...
[STARTED] Running tasks...
[STARTED] Running tasks for *.ts
[STARTED] npm run lint-strict
[FAILED] npm run lint-strict [FAILED]
[FAILED] npm run lint-strict [FAILED]
[SUCCESS] Running tasks...
[STARTED] Applying modifications...
[SKIPPED] Skipped because of errors from tasks.
[STARTED] Reverting to original state because of errors...
[SUCCESS] Reverting to original state because of errors...
[STARTED] Cleaning up...
[SUCCESS] Cleaning up...
× npm run lint-strict:
ESLint found too many warnings (maximum: 0).
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! algorithms-utils@1.0.0 lint-strict: `eslint src --max-warnings 0 "C:/Code/Git/algorithms/src/greet.ts"`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the algorithms-utils@1.0.0 lint-strict script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\子弈\AppData\Roaming\npm-cache\_logs\2020-07-11T07_25_39_102Z-debug.log
> algorithms-utils@1.0.0 lint-strict C:\Code\Git\algorithms
> eslint src --max-warnings 0 "C:/Code/Git/algorithms/src/greet.ts"
C:\Code\Git\algorithms\src\greet.ts
2:16 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types
2:34 warning Argument 'name' should be typed @typescript-eslint/explicit-module-boundary-types
✖ 2 problems (0 errors, 2 warnings)
husky > pre-commit hook failed (add --no-verify to bypass)
husky > pre-commit (node v12.13.1)
[STARTED] Preparing...
[SUCCESS] Preparing...
[STARTED] Running tasks...
[STARTED] Running tasks for *.ts
[STARTED] npm run lint
[SUCCESS] npm run lint
[STARTED] npm run jest
[FAILED] npm run jest [FAILED]
[FAILED] npm run jest [FAILED]
[SUCCESS] Running tasks...
[STARTED] Applying modifications...
[SKIPPED] Skipped because of errors from tasks.
[STARTED] Reverting to original state because of errors...
[SUCCESS] Reverting to original state because of errors...
[STARTED] Cleaning up...
[SUCCESS] Cleaning up...
× npm run jest:
FAIL test/greet.spec.ts
src/greet.ts
× name param test (4 ms)
● src/greet.ts › name param test
expect(received).toBe(expected) // Object.is equality
Expected: "Hello from world 1"
Received: "Hello from world"
3 | describe("src/greet.ts", () => {
4 | it("name param test", () => {
> 5 | expect(greet("world")).toBe("Hello from world 1");
| ^
6 | });
7 | });
8 |
at Object.<anonymous> (test/greet.spec.ts:5:28)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 1.339 s, estimated 3 s
Ran all test suites related to files matching /C:\\Code\\Git\\algorithms\\src\\index.ts|C:\\Code\\Git\\algorithms\\test\\greet.spec.ts/i.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! algorithms-utils@1.0.0 jest: `jest --bail --findRelatedTests --coverage "C:/Code/Git/algorithms/src/index.ts" "C:/Code/Git/algorithms/test/greet.spec.ts"`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the algorithms-utils@1.0.0 jest script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\子弈\AppData\Roaming\npm-cache\_logs\2020-07-12T14_33_51_183Z-debug.log
> algorithms-utils@1.0.0 jest C:\Code\Git\algorithms
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the algorithms-utils@1.0.0 jest script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\子弈\AppData\Roaming\npm-cache\_logs\2020-07-12T14_33_51_183Z-debug.log
> algorithms-utils@1.0.0 jest C:\Code\Git\algorithms
> jest --bail --findRelatedTests --coverage "C:/Code/Git/algorithms/src/index.ts" "C:/Code/Git/algorithms/test/greet.spec.ts"
----------|---------|----------|---------|---------|-------------------
| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
| ---------- | --------- | ---------- | --------- | --------- | ------------------- |
| All files | 0 | 0 | 0 | 0 |
| ---------- | --------- | ---------- | --------- | --------- | ------------------- |
husky > pre-commit hook failed (add --no-verify to bypass)
git exited with error code 1
import greet from "@/greet";
describe("src/greet.ts", () => {
it("name param test", () => {
expect(greet("world")).toBe("Hello from world 1");
});
});
// 这里输入了重复的 title
describe("src/greet.ts", () => {
it("name param test", () => {
expect(greet("world")).toBe("Hello from world 1");
});
});
需要注意修改 package.json 中的 ESLint 校验范围:
"scripts": {
// 这里对 src 和 test 目录进行 ESLint 校验
"lint": "eslint src test --max-warnings 0",
},
执行 npm run lint 进行单元测试的格式校验:
PS C:\Code\Git\algorithms> npm run lint
> algorithms-utils@1.0.0 lint C:\Code\Git\algorithms
> eslint src test --max-warnings 0
C:\Code\Git\algorithms\test\greet.spec.ts
9:10 error Describe block title is used multiple times in the same describe block jest/no-identical-title
✖ 1 problem (1 error, 0 warnings)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! algorithms-utils@1.0.0 lint: `eslint src test --max-warnings 0`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the algorithms-utils@1.0.0 lint script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\子弈\AppData\Roaming\npm-cache\_logs\2020-07-13T02_25_12_043Z-debug.log
PS C:\Code\Git\algorithms> npm run docs:dev
> algorithms-utils@1.0.0 docs:dev C:\Code\Git\algorithms
> vuepress dev docs
wait Extracting site metadata...
tip Apply theme @vuepress/theme-default ...
tip Apply plugin container (i.e. "vuepress-plugin-container") ...
tip Apply plugin @vuepress/register-components (i.e. "@vuepress/plugin-register-components") ...
tip Apply plugin @vuepress/active-header-links (i.e. "@vuepress/plugin-active-header-links") ...
tip Apply plugin @vuepress/search (i.e. "@vuepress/plugin-search") ...
tip Apply plugin @vuepress/nprogress (i.e. "@vuepress/plugin-nprogress") ...
√ Client
Compiled successfully in 5.31s
i 「wds」: Project is running at http://0.0.0.0:8080/
i 「wds」: webpack output is served from /algorithms-utils/
i 「wds」: Content not from webpack is served from C:\Code\Git\algorithms\docs\.vuepress\public
i 「wds」: 404s will fallback to /index.html
success [23:13:14] Build 10b15a finished in 5311 ms!
> VuePress dev server listening at http://localhost:8080/algorithms-utils/
由于本项目依赖 Github,因此没法使用 Gitlab 默认集成的能力。之前的 Github 项目采用了 Travis 进行项目的 CI / CD 集成,现在因为有了更方便的 Github Actions,因此决定采用 Github 自带的 Actions 进行 CI / CD 能力集成(大家如果想更多了解这些 CI / CD 的差异请自行 Google 哈)。Github Actions 所带来的好处在于:
可复用的 Actions(以前你需要写复杂的脚本,现在可以复用别人写好的脚本,可以简单理解为 CI 脚本插件化)
前言
本文是算法与 TypeScript 实现中 TypeScript 项目整体的环境配置过程介绍。主要包括了以下一些配置内容:
如果你对以上的某些配置非常熟悉,则可以跳过阅读。如果你不清楚是否要继续阅读其中的一些配置信息,则可以通过工程问题来决定是否要继续阅读相关的内容。
算法与 TypeScript 实现 关于当前配置的改造在 feat/framework 分支上,希望刚兴趣的同学可以 star 一波。学习文档 目前仍然是老版本的学习文档,之后会进行持续更新。
配置问题
希望你读完这篇文章能够了解以下一些问题(很有可能成为工程配置方面的面试题哦,细节决定成败):
pre-commit
和commit-msg
钩子的区别是什么?各自可用于做什么?除此之外如果你对其他相关的知识感兴趣(非本文相关的知识),希望你能额外深入去探索:
配置框架
需要注意文档中的配置说明可能会省略某些细节步骤(例如某些依赖的 npm 包安装、某些配置文件说明等),如果想要知道更多细节信息,可查看各个配置的 Commit 提交信息:
Git Commit Message
Commitizen 是一个规范 Git 提交说明(Commit Message)的 CLI 工具,具体如何配置可查看 Cz 工具集使用介绍(这篇文章对于 Commit Message 的配置介绍已经非常详细清楚,因此这里不再过多介绍)。本项目中主要使用了以下一些工具:
配置后会产生以下一些特性:
git cz
代替git commit
进行符合 Angular 规范的 Commit Message 信息提交npm run changelog
会在根目录下自动生成CHANGELOG.md
版本日志例如当你提交了一个不符合规范的 Commit Message(此时提交失败):
TypeScript
TypeScript 背景
工具函数库的实现采用 TypeScript,除了可以自动生成 ts 声明文件供外部更好的提示使用之外,也可以避免 JavaScript 动态性所带来的一些无法预料的错误信息(具体可查看 Top 10 JavaScript errors from 1000+ projects (and how to avoid them)),从而使算法的设计更加严谨。 TypeScript 的构建方式有很多种,除了原生编译器 tsc 以外,还包括 Webpack、Rollup、Babel 以及 Gulp 等(更多构建工具的集成可查看 Integrating with Build Tools):
由于算法的函数工具库功能非常单一简单,因此采用 TypeScript 官方推荐的 Gulp 工具进行构建即可满足需求。
TypeScript 配置
本项目会构建输出 CommonJS 工具包(npm 包)供外部使用,采用 TypeScript 设计并输出声明文件有助于外部更好的使用该资源包进行 API 的提示。TypeScript 编译采用官方文档推荐的 Gulp 工具并配合 gulp-typescript 和 tsconfig.json 配置文件。在根目录下新建
tsconfig.json
文件并新增以下配置:同时在根目录下新建
gulpfile.js
文件:在
package.json
中新增 npm script 脚本:其中 rimfaf 用于在构建之前清除
dist
目录文件内容。此时在src
目录下新增 TypeScript 源码并使用npm run build
命令可以进行项目构建并输出 CommonJS 规范的目标代码到dist
目录下。除此之外,此项目希望可以快速生成声明文件供外部进行代码提示,此时仍然可以借助
gulp-typescript
工具自动生成声明文件。在gulpfile.js
中新增以下配置修改
build
命令使其在构建之前同时可以删除types
目录:再次执行
npm run build
会在项目根目录下生成types
文件夹,该文件夹主要存放自动生成的 TypeScript 声明文件。需要注意发布 npm 包时默认会将当前项目的所有文件进行发布处理,但这里希望发布的包只包含使用者需要的编译文件
dist
和types
,因此可以通过package.json
中的files
(用于指定发布的 npm 包包含哪些文件) 字段信息进行控制:除此之外,如果希望发布的 npm 包通过
require('algorithms-utils')
或import
形式引入时指向dist/index.js
文件,需要配置package.json
中的main
字段信息:ESLint
ESLint 背景
TypeScript 的代码检查工具主要有 TSLint 和 ESLint 两种。早期的 TypeScript 项目一般采用 TSLint 进行检查。TSLint 和 TypeScript 采用同样的 AST 格式进行编译,但主要问题是对于 JavaScript 生态的项目支持不够友好,因此 TypeScript 团队在 2019 年宣布全面转向 ESLint(具体可查看 TypeScript 官方仓库的
.eslintrc.json
配置),更多关于转向 ESLint 的原因可查看:TypeScript 和 ESLint 使用不同的 AST 进行解析,因此为了在 ESLint 中支持 TypeScript 代码检查需要制作额外的自定义解析器(Custom Parsers,ESLint 的自定义解析器功能需要基于 ESTree),目的是为了能够解析 TypeScript 语法并转成与 ESLint 兼容的 AST。@typescript-eslint/parser 在这样的背景下诞生,它会处理所有 ESLint 特定的配置并调用 @typescript-eslint/typescript-estree 生成 ESTree-compatible AST(需要注意不仅仅兼容 ESLint,也能兼容 Prettier)。
@typescript-eslint
是一个采用 Lerna 进行设计的 Monorepo 结构仓库,除了上述提到的 npm 包之外,还包含以下两个重要的 npm 包:@typescript-eslint/parser
一起使用的 ESLint 插件,可以设置 TypeScript 的校验规则。ESLint 配置
从背景的介绍中可以理解,对于全新的 TypeScript 项目(直接抛弃 TSLint)需要包含解析 AST 的解析器 @typescript-eslint/parser 和使用校验规则的插件 @typescript-eslint/eslint-plugin,这里需要在项目中进行安装:
在根目录新建
.eslintrc.js
配置文件,并设置以下配置:其中:
parser: '@typescript-eslint/parser'
:使用 ESLint 解析 TypeScript 语法plugins: ['@typescript-eslint']
:在 ESLint 中加载插件@typescript-eslint/eslint-plugin
,该插件可用于配置 TypeScript 校验规则。extends: [ ... ]
:在 ESLint 中使用共享规则配置,其中eslint:recommended
是 ESLint 内置的推荐校验规则配置(也被称作最佳规则实践),plugin:@typescript-eslint/recommended
是类似于eslint:recommended
的 TypeScript 推荐校验规则配置。配置完成后在
package.json
中设置校验命令此时如果在
src
目录下书写错误的语法,执行npm run lint
就会输出错误信息:ESLint 插件
如果不使用插件,很难发现代码可能存在 TypeScript 格式错误,因为在书写代码的时候除了手动执行
npm run lint
以外没有任何的实时提示信息(你当然也可以通过gulp
监听文件的变化并执行npm run lint
)。为了可以实时看到 TypeScript 错误信息,可以通过 VS Code 插件进行处理。安装 ESLint 插件后可进行代码的实时提示,具体如下图所示:当然为了防止不需要被校验的文件出现校验信息,可以通过
.eslintignore
文件进行配置(例如以下都是一些不需要格式校验的配置文件):此时可以发现之前执行
lint
命令的错误通过插件的形式可实时在 VS Code 编辑器中进行显示。除此之外,一些 ESLint 的格式校验错误(例如多余的;
等)可通过配置 Save Auto Fix 进行保存自动格式化处理。具体 VS Code 的配置可参考 ESLint 插件 的文档说明,这边应该需要进行如下配置:ESLint 确保构建
VS Code 插件并不能确保代码上传或构建前无任何错误信息,此时仍然需要额外的流程能够避免错误。在构建前进行 ESLint 校验能够确保构建时无任何错误信息,一旦 ESLint 校验不通过则不允许进行源码的构建操作:
需要注意在构建时进行校验的严格控制,一旦 lint 抛出 warning 或者 error 则立马终止构建(详情可查看 ESLint 退出代码)。
ESLint 确保代码上传
尽管可能配置了 ESLint 的校验脚本 以及 VS Code 插件,但是有些 ESLint 的规则校验是无法通过 Save Auto Fix 进行格式化修复的(例如质量规则),因此还需要一层保障能够确保代码提交之前所有的代码能够通过 ESLint 校验,这个配置将在 Lint Staged 中进行讲解。
Prettier
Prettier 背景
Prettier 是一个统一代码格式风格的工具,如果你不清楚为什么需要使用 Prettier,可以查看 Why Prettier?。很多人可能疑惑,ESLint 已经能够规范我们的代码风格,为什么还需要 Prettier?在 Prettier vs Linters 中详细说明了两者的区别,Linters 有两种类型的规则:
ESLint 的规则校验同时包含了 格式规则 和 质量规则,但是大部分情况下只有 格式规则 可以通过
--fix
或 VS Code 插件的 Sava Auto Fix 功能一键修复,而 质量规则 更多的是发现代码可能出现的 Bug 从而防止代码出错,这类规则往往需要手动修复。因此 格式规则 并不是必须的,而 质量规则 则是必须的。Prettier 与 ESLint 的区别在于 Prettier 专注于统一的格式规则,从而减轻 ESLint 在格式规则上的校验,而对于质量规则 则交给专业的 ESLint 进行处理。总结一句话就是:Prettier for formatting and linters for catching bugs!(ESLint 是必须的,Prettier 是可选的!)需要注意如果 ESLint(TSLint) 和 Prettier 配合使用时格式规则有重复且产生了冲突,那么在编辑器中使用 Sava Auto Fix 时会让你的一键格式化哭笑不得。此时应该让两者把各自注重的规则功能区分开,使用 ESLint 校验质量规则,使用 Prettier 校验格式规则,更多信息可查看 Integrating with Linters。
Prettier 配置
首先安装 Prettier 所需要的依赖:
其中:
@typescript-eslint/eslint-plugin
、eslint-plugin-babel
、eslint-plugin-react
、eslint-plugin-vue
、eslint-plugin-standard
等格式规则。理论上而言,在项目中开启 ESLint 的
extends
中设置的带有格式规则校验的规则集,那么就需要通过eslint-config-prettier
插件关闭可能产生冲突的格式规则:配置完成后,可以通过命令行接口运行 Prettier:
--write
参数类似于 ESLint 中的--fix
(在 ESLint 中使用该参数还是需要谨慎哈,建议还是使用 VS Code 的 Save Auto Fix 功能),主要用于自动修复格式错误。此时书写格式的错误代码:执行
npm run prettier
进行格式修复:修复之后的的文件格式如下:
需要注意如果某些规则集没有对应的
eslint-config-prettier
关闭配置,那么可以先通过 CLI helper tool 检测是否有重复的格式规则集在生效,然后可以通过手动配置eslintrc.js
的形式进行关闭:例如把
eslint-config-prettier
的配置去除,此时进行检查重复规则:此时假设
eslint-config-prettier
没有类似的关闭格式规则集(例如本项目中配置的plugin:jest/recommended
可能存在规则冲突),那么可以通过配置.eslintrc.js
的形式自己手动关闭相应冲突的格式规则。Prettier 插件
通过命令行接口
--write
的形式可以进行格式自动修复,但是类似 ESLint,我们更希望项目在实时编辑时可以通过保存就能自动格式化代码(鬼知道--fix
以及--write
格式了什么文件,当然更希望通过肉眼的形式立即感知代码的格式化变化),此时可以通过配置 VS Code 的 Prettier - Code formatter 插件进行 Save Auto Fix,具体的配置查看插件文档。Prettier 确保代码上传
和 ESLint 一样,尽管可能配置了 Prettier 的自动修复格式脚本以及 VS Code 插件,但是无法确保格式遗漏的情况,因此还需要一层保障能够确保代码提交之前能够进行 Prettier 格式化,这个配置将在 Lint Staged 中讲解,更多配置方案也可以查看 Prettier - Pre-commit Hook。
Lint Staged
Lint Staged 背景
在 Git Commit Message 中使用了 commitlint 工具配合 husky 可以防止生成不规范的 Git Commit Message,从而阻止用户进行不规范的 Git 代码提交,其原理就是监听了 Git Hook 的执行脚本(会在特定的 Git 执行命令诸如
commit
、push
、merge
等触发之前或之后执行相应的脚本钩子)。Git Hook 其实是进行项目约束非常好用的工具,它的作用包括但不限于:Git Hook 的钩子非常多,但是在客户端中可能常用的钩子是以下两个:
pre-commit
:Git 中pre
系列钩子允许终止即将发生的 Git 操作,而post
系列往往用作通知行为。pre-commit
钩子在键入提交信息(运行git commit
或git cz
)前运行,主要用于检查当前即将被提交的代码快照,例如提交遗漏、测试用例以及代码等。该钩子如果以非零值退出则 Git 将放弃本次提交。当然你也可以通过配置命令行参数git commit --no-verify
绕过钩子的运行。commit-msg
:该钩子在用户输入 Commit Message 后被调用,接收存有当前 Commit Message 信息的临时文件路径作为唯一参数,因此可以利用该钩子来核对 Commit Meesage 信息(在 Git Commit Message 中使用了该钩子对提交信息进行了是否符合 Angular 规范的校验)。该钩子和pre-commit
类似,一旦以非零值退出 Git 将放弃本次提交。除了上述常用的客户端钩子,还有两个常用的服务端钩子:
pre-receive
:该钩子会在远程仓库接收git push
推送的代码时执行(注意不是本地仓库),该钩子会比pre-commit
更加有约束力(总会有这样或那样的开发人员不喜欢提交代码时所做的一堆检测,他们可能会选择绕过这些钩子)。pre-receive
钩子可用于接收代码时的强制规范校验,如果某个开发人员采用了绕过pre-commit
钩子的方式提交了一堆 💩 一样的代码,那么通过设置该钩子可以拒绝代码提交。当然该钩子最常用的操作还是用于检查是否有权限推送代码、非快速向前合并等。post-receive
:该钩子在推送代码成功后执行,适合用于发送邮件通知或者触发 CI 。需要注意初始化 Git 之后默认会在
.git/hooks
目录下生成所有 Git 钩子的 Shell 示例脚本,这些脚本是可以被定制化的。对于前端开发而言去更改这些示例脚本适配前端项目非常不友好(大多数前端开发同学压根不会设计 Shell 脚本,尽管这个对于制作工具是一件非常高效的事情),因此社区就出现了类似的增强工具,它们对外抛出的是简单的钩子配置(例如 ghooks 在package.json
中只需要进行简单的钩子属性配置),而在内部则通过替换 Git 钩子示例脚本的形式使得外部配置的钩子可以被执行,例如 husky、ghooks 以及 pre-commit 等。介绍 Git Hook 是为了让大家清晰的认知到使用 Hook 可以在前端的工程化项目中做很多事情(本来应该放在 Git Commit Message 中介绍相对合适,但是鉴于那个小节引用了另外一篇文章,因此将这个信息放在本小节进行科普)。
之前提到使用 Git Hook 可以进行 ESLint 规范约束,因此大家其实应该能够猜到使用
pre-commit
钩子(当然需要借助 Git Hook 增强工具,本项目中一律选择husky
)配合 ESLint 可以进行提交说明前的项目代码规则校验,但是如果项目越来越大,ESLint 校验的时间可能越来越长,这对于频繁的代码提交者而言可能是一件相对痛苦的事情,因此可以借助lint-staged
工具(听这个工具的名字就能够猜测 lint 的是已经放入 Git Stage 暂存区中的代码,ed
在英文中表明已经做过)减少代码的检测量。Lint Staged 配置
使用 commitlint 工具可以防止生成不规范的 Git Commit Message,从而阻止用户进行 Git 代码提交。但是如果想要防止团队协作时开发者提交不符合 ESLint 规则的代码则可以通过 lint-staged 工具来实现。
lint-staged
可以在用户提交代码之前(生成 Git Commit Message 信息之前)使用 ESLint 检查 Git 暂存区中的代码信息(git add
之后的修改代码),一旦存在 💩 一样不符合校验规则的代码,则可以终止提交行为。需要注意的是lint-staged
不会检查项目的全量代码(全量使用 ESLint 校验对于较大的项目可能会是一个相对耗时的过程),而只会检查添加到 Git 暂存区中的代码。根据官方文档执行以下命令自动生成配置项信息:需要注意默认生成的配置文件是针对 JavaScript 环境的,手动修改
package.json
中的配置信息进行 TypeScript 适配:此时如果将要提交的代码有 💩 , 则提交时会提示错误信息且提交会被强制终止:
husky 在
package.json
中配置了pre-commit
和commit-msg
两个 Git 钩子,优先使用pre-commit
钩子执行 ESLint 校验,如果校验失败则终止运行。如果校验成功则会继续执行commit-msg
校验 Git Commit Message,例如以下是 ESLint 校验通过但是 Commit Message 校验失败的例子:Jest
测试背景
如果对于测试的概念和框架不是特别清楚,这里推荐一些可查看的文章:
除此之外,如果想了解一些额外的测试技巧,这里推荐一些社区的最佳实践:
由于这里只是 Node 环境工具库包的单元测试,在对比了各个测试框架之后决定采用 Jest 进行单元测试:
it
到expect
, Jest 将整个工具包放在一个地方)Jest 配置
本项目的单元测试主要采用了 Jest 测试框架。Jest 如果需要对 TypeScript 进行支持,可以通过配合 Babel 的形式,具体可查看 Jest - Using TypeScript,但是采用 Babel 会产生一些限制(具体可查看 Babel 7 or TypeScript)。由于本项目没有采用 Babel 进行转译,并且希望能够完美支持类型检查,因此采用 ts-jest 进行单元测试。按照官方教程进行依赖安装和项目初始化:
子啊根目录的
ject.config.js
文件中进行 Jest 配置修改:需要注意路径映射也需要配置
tsconfig.json
中的paths
信息,同时注意将测试代码包含到 TypeScript 的编译目录中。配置完成后在package.json
中配置测试命令:需要注意 Jest 中的这些配置信息(更多配置信息可查看 Jest CLI Options):
bail
的配置作用相对类似于 ESLint 中的max-warnings
,设置为true
则表明一旦发现单元测试用例错误则停止运行其余测试用例,从而可以防止运行用例过多时需要一直等待用例全部运行完毕的情况。coverage
主要用于在当前根目录下生成coverage
代码的测试覆盖率报告,该报告还可以上传 coveralls 进行 Github 项目的 Badges 显示。在当前根目录的
test
目录下新建greet.spec.ts
文件,并设计以下测试代码:Jest 确保构建
单独通过执行
npm run test
命令进行单元测试,这里演示执行构建命令时的单元测试(需要保证构建之前所有的单元测试用例都能通过)。如果测试失败,那么应该防止继续构建,例如进行失败的构建行为:需要注意由于是并行(
&&
)执行脚本,因此执行构建命令时(npm run build
)会先执行 ESLint 校验,如果 ESLint 校验失败那么退出构建,否则继续进行 Jest 单元测试。如果单元测试失败那么退出构建,只有当两者都通过时才会进行源码构建。Jest 确保代码上传
除了预防不负责任的代码构建以外,还需要预防不负责任的代码提交。配合
lint-staged
可以防止未跑通单元测试的代码进行远程提交:此时如果单元测试有误,都会停止代码提交:
Jest 对于 ESLint 支持
src
目录下的源码通过配置@typescript-eslint/eslint-plugin
可进行推荐规则的 ESLint 校验,为了使得test
目录下的测试代码能够进行符合 Jest 推荐规则的 ESLint 校验,可以通过配置 eslint-plugin-jest 进行支持(ts-jest
项目就是采用了该插件进行 ESLint 校验,具体可查看配置文件ts-jest/.eslintrc.js
),这里仍然采用推荐的规则配置:为了验证推荐规则是否生效,这里可以找一个
no-identical-title
规则进行验证:需要注意修改
package.json
中的 ESLint 校验范围:执行
npm run lint
进行单元测试的格式校验:此时会发现 ESLint 抛出了相应的错误信息。需要注意采用此 ESLint 校验之后也会在 VS Code 中实时生成错误提示(相应的代码下会有红色波浪线,鼠标移入后会产生 Tooltip 提示该错误的相应规则信息,除此之外当前工程目录下对应的文件名也会变成红色),此后的 Git 提交以及 Build 构建都会失败!
Npm Script Hook
当你查看前端开源项目时第一时间可能会找
package.json
中的main
、bin
以及files
等字段信息,除此之外如果还想深入了解项目的结构,可能还会查看scripts
脚本字段信息用于了解项目的开发、构建、测试以及安装等流程。npm 的脚本功能非常强大,你可以利用脚本制作项目需要的任何流程工具。本文不会过多介绍 npm 脚本的功能,只是讲解一下其中用到的 钩子 功能。目前在本项目中使用的一些脚本命令如下(就目前而言脚本相对较少,定义还蛮清晰的):
重点看下
build
脚本命令,会发现这个脚本命令包含了大量的继发执行脚本,但真正和build
相关的只有rimraf dist types && gulp
这两个脚本。这里通过 npm 的脚本钩子pre
和post
将脚本的功能区分开,从而使脚本的语义更加清晰(当然脚本越来越多的时候也可能容易增加开发者的认知负担)。npm 除了指定一些特殊的脚本钩子以外(例如prepublish
、postpublish
、preinstall
、postinstall
等),还可以对任意脚本增加pre
和post
钩子,这里通过自定义钩子将并发执行的脚本进行简化:此时如果执行
npm run build
命令时事实上类似于执行了以下命令:之后设计的脚本如果继发执行繁多,那么都会采用 npm scripts hook 进行设计。
Vuepress
Vuepress 背景
一般组件库或工具库都需要设计一个演示文档(提供良好的开发体验)。一般的工具库可以采用 tsdoc、jsdoc 或 esdoc 等工具进行 API 文档的自动生成,但往往需要符合一些注释规范,这些注释规范在某种程度上可能会带来开发负担,当然也可以交给 VS Code 的插件进行一键生成,例如 Document This For jsdoc 或 TSDoc Comment。
组件库 Element UI 采用 vue-markdown-loader(Convert Markdown file to Vue Component using markdown-it) 进行组件的 Demo 演示设计,但是配置相对复杂。更简单的方式是配合 Vuepress 进行设计,它的功能非常强大,但前提是熟悉 Vue,因为可以在 Markdown 中使用 Vue 语法。当然如果是 React 组件库的 Demo 演示,则可以采用 dumi 生成组件 Demo 演示文档(不知道没有更加好用的类 Vuepress 的 React 组件文档生成器, 更多和 React 文档相关也可以了解 react-markdown、react-static 等)。
由于之前采用过 Vuepress 设计 Vue 组件库的 Demo 演示文档,因此这里仍然沿用它来设计工具库包的 API 文档(如果你想自动生成 API 文档,也可以额外配合 tsdoc 工具)。采用 Vuepress 设计文档的主要特点如下:
Vuepress 配置
先按照官方的 快速上手 文档进行依赖安装和 npm scripts 脚本设置:
按照 Vuepress 官网约定优于配置的原则进行演示文档的目录结构设计,官方的文档可能一下子难以理解,可以先设计一个最简单的目录:
根据默认主题 / 首页在
docs/README.md
进行首页设计:根据配置 对
docs/.vuepress/config.js
文件进行基本配置:此时通过
npm run docs:dev
进行开发态文档预览:效果如下:
当然除了以上设计的首页,在本项目中还会设计导航栏、侧边栏、使用插件、使用组件等。这里重点讲解一下 Webpack 构建 配置。
为了在 Markdown 文档中可以使用
src
目录的 TypeScript 代码,这里对.vuepress/config.js
文件进行配置处理:此时可以在 Vuepress 的 Markdown 文档中进行 TypeScript 引入的演示文档设计:
启动 Vuepress 查看演示文档:
可以发现在 Markdown 中引入的
src/greet.ts
代码生效了,最终通过npm run docs:build
可以生成演示文档的静态资源进行部署和访问。文档工具和规范
通常在书写文档的时候很多同学都不注重文档的洁癖,其实书写文档和书写代码一样需要一些格式规范。markdownlint 是类似于 ESLint 的 Markdown 格式校验工具,通过它可以更好的规范我们书写的文档。当然 Markdown 的格式校验不需要像 ESLint 或者 Prettier 校验那样进行强约束,简单的能够做到提示和 Save Auto Fix 即可。
通过安装 Vs Code 插件 markdownlint 并进行 Save Auto Fix 配置(在插件中明确列出了哪些规则是可以被 Fix 的)。安装完成后查看刚刚进行的测试文件:
此时会发现插件生效了,但是在 Markdown 中插入 html 是必须的一个能力(Vuepress 支持的能力就是在 Markdown 中使用 Vue),因此可以通过
.markdownlintrc
文件将相应的规则屏蔽掉。Github Actions
CI / CD 背景
关于 CI / CD 的背景这里就不再过多介绍,有兴趣的同学可以看看以下一些好文:
在 Introduction to CI/CD with GitLab(中文版) 中你可以清晰的了解到 CI 和 CD 的职责功能:
通过以下图可以更清晰的发现 Gitlab 在每个阶段可用的功能:
由于本项目依赖 Github,因此没法使用 Gitlab 默认集成的能力。之前的 Github 项目采用了 Travis 进行项目的 CI / CD 集成,现在因为有了更方便的 Github Actions,因此决定采用 Github 自带的 Actions 进行 CI / CD 能力集成(大家如果想更多了解这些 CI / CD 的差异请自行 Google 哈)。Github Actions 所带来的好处在于:
当然也会产生一些限制,这些限制主要是和执行时间以及次数相关。需要注意类似于 Jenkins 等支持本地连接运行,Github Actions 也支持连接到本地机器运行 workflow,因此部分限制可能不受本地运行的限制。
Github Actions 配置
本项目的配置可能会包含以下三个方面:
这里主要讲解自动更新静态资源流程,大致需要分为以下几个步骤(以下都是在 Github 服务器上进行操作,你可以理解为新的服务环境):
通过查看 官方 Actions 和 awesome-actions,找到所需的 Actions:
$GITHUB_WORKSPACE
目录下在
.github/workflows
下新增mian.yml
配置文件:上传 CI 的配置文件后,Github 就会进行自动构建,具体如下:
正在构建或者构建完成后可查看每个构建的信息,如果初次构建失败则可以通过构建信息找出失败原因,并重新修改构建配置尝试再次构建。除此之外,每次构建失败 Github 都会通过邮件的形式进行通知:
如果构建成功,则每次你推送新的代码后,Github 服务会进行一系列流程并自动更新静态资源站点。
总结
希望大家看完这篇文档之后如果想使用其中某些工具能够养成以下一些习惯:
通篇阅读工具对应的官方 Github README 文档以及官方站点文档,了解该工具设计的核心哲学、核心功能、解决什么核心问题。前端的工具百花齐放,同样的功能可能可以采用多种不同的工具实现。如果想要在项目中使用适当的工具,就得知道这些工具的差异。完整的阅读相应的官方文档,有助于你理解各自的核心功能和差异。
在实践的过程中你会对该工具的使用越来越熟悉。此时如果遇到一些问题或者想要实现某些功能,在通篇阅读文档的基础上会变得相对容易。当然如果遇到一些报错信息无法解决,此时第一时间应该是搜索当前工具所对应的 Github Issues。除此之外,你也可以根据错误的堆栈信息追踪工具的源码,了解源码之后可能会对错误信息产生的原因更加清晰。
链接文档