本文出自於掘金的子弈[1],原文連結 從零開始配置 TypeScript 項目[2]。以下是他的一些不錯的作品,感興趣的可以閱讀:
面試分享:兩年工作經驗成功面試阿里 P6 總結[3]如果覺得不錯希望能夠在掘金關注、點讚哦,有機會一起睡覺~
前言本文是算法與 TypeScript 實現[5]中 TypeScript 項目整體的環境配置過程介紹。主要包括了以下一些配置內容:
如果你對以上的某些配置非常熟悉,則可以跳過閱讀。如果你不清楚是否要繼續閱讀其中的一些配置信息,則可以通過工程問題來決定是否要繼續閱讀相關的內容。
算法與 TypeScript 實現[6] 關於當前配置的改造在 feat/framework[7] 分支上,希望剛興趣的同學可以 star 一波。學習文檔[8] 目前仍然是老版本的學習文檔,之後會進行持續更新。
溫馨提示:如果你希望在項目中製作基於 TypeScript 實現的簡單易用的工具函數庫,你可以使用一些成熟的 "零配置" 腳手架,例如 tsdx[9]、microbundle[10] 以及 typescript-starter[11] 等。如果功能不能滿足你的項目需求,你也可以基於這些工具進行團隊的定製化改造,例如 ts-lib-scripts[12]。
配置問題希望你讀完這篇文章能夠了解以下一些問題(很有可能成為工程配置方面的面試題哦,細節決定成敗):
在使用 Git 的時候如何規範 Git 的提交說明(Commit 信息)?簡述符合 Angular 規範的提交說明的結構組成?Commit 信息如何和 Github Issues 關聯?TypeScript 如何自動生成庫包的聲明文件?TypeScript 目前是採用 TSLint 還是 ESLint 進行代碼校驗,為什麼?列舉你知道的所有構建工具並說說這些工具的優缺點?這些構建工具在不同的場景下應該如何選型?Babel 對於 TypeScript 的支持有哪些限制?如何確保構建和上傳的代碼無 ESLint 錯誤信息?ESLint 和 Prettier 的區別是什麼?兩者在一起工作時會產生問題嗎?如何有效的識別 ESLint 和 Prettier 可能產生衝突的格式規則?如何解決此類規則衝突問題?git hook 中客戶端和服務端鉤子各自用於什麼作用?pre-commit 和 commit-msg 鉤子的區別是什麼?各自可用於做什麼?husky 以及 ghook 等工具製作 git hook 的原理是什麼?git hook 可以採用 Node 腳本進行設計嗎?如何做到?談談你所理解的 npm scripts,它有哪些功能?什麼是 e2e 測試?有哪些 e2e 的測試框架?假設現在有一個插入排序算法,如何對該算法進行單元測試?假設你自己實現的 React 或 Vue 的組件庫要設計演示文檔,你會如何設計?設計的文檔需要實現哪些功能?在設計工具庫包的時候你是如何設計 API 文檔的?在通常的腳手架項目中進行熱更新(hot module replacement)時如何做到 ESLint 實時列印校驗錯誤信息?你所知道的 CI / CD 工具有哪些?在項目中有接觸過類似的流程嗎?除此之外如果你對其他相關的知識感興趣(非本文相關的知識),希望你能額外深入去探索:
CommonJS 和 ES Module 有哪些區別?Tree Shaking 的作用是什麼?什麼情況下可以使用 Tree Shaking 的能力?如何引入 ES Module 庫包?在構建層面和包描述文件層面需要注意哪些方面?談談你對 TypeScript 聲明文件的理解?在製作庫包時如何對外識別聲明文件?在外部使用時有哪些好處?在製作工具包的時候如何考慮按需引入和全量引入的優雅引入設計?了解 Vue CLI 3.x 的功能特點嗎?如何基於 Vue CLI 3.x 定製符合團隊項目的腳手架?工程化配置領域的設計可以有哪些設計階段(例如 react-scripts 和 vue ui 在設計以及使用形態上的區別)?工程化配置監控(使用版本信息、版本兼容性報錯信息分析、使用功能分析等)?溫馨提示:有些問題在本文中能夠得到答案,有些問題需要自己擴展閱讀或查看源碼才能得到答案(作者同樣是工程化配置領域的小白,以上的這些問題同樣在問自己)。
配置框架需要注意文檔中的配置說明可能會省略某些細節步驟(例如某些依賴的 npm 包安裝、某些配置文件說明等),如果想要知道更多細節信息,可查看各個配置的 Commit 提交信息:
framework: 新增 Git Commit Message 規範提交能力 (d04e259[14])framework: 新增 TypeScript 編譯能力 (ebecee9[15])framework: 新增 ESLint 代碼校驗能力 (dca67d4[16])framework: 新增 Prettier 自動格式化能力 (7f3487a[17])framework: 新增 Lint Staged 上傳校驗能力 (b440186[18])framework: 新增 Jest 單元測試能力 (6f086f2[19])framework: 新增 Npm Scripts Hook 能力 (93e597a[20])framework: 新增 Vuepress 演示文檔能力 (66e38d1[21])framework: 新增 Github Actions 能力 (1cc85a4[22])溫馨提示:以上都是使用 npm run changelog 自動生成的版本日誌信息,你也可以通過倉庫的 CHANGELOG.md[23] 進行查看。
Git Commit MessageCommitizen[24] 是一個規範 Git 提交說明(Commit Message)的 CLI 工具,具體如何配置可查看 Cz 工具集使用介紹[25](這篇文章對於 Commit Message 的配置介紹已經非常詳細清楚,因此這裡不再過多介紹)。本項目中主要使用了以下一些工具:
conventional-changelog[28]配置後會產生以下一些特性:
使用 git cz 代替 git commit 進行符合 Angular 規範的 Commit Message 信息提交代碼提交之前會通過 husky[29] 配合 git hook 進行提交信息校驗,一旦提交信息不符合 Angular 規範,則提交會失敗執行 npm run changelog 會在根目錄下自動生成 CHANGELOG.md 版本日誌溫馨提示:husky 中文的意思是哈士奇,大家可以想像一下為什麼這個工具叫哈士奇,是不是咬著你不放的意思(or more 🐶 woof!),一旦它咬著你的代碼提交不放,這將會是非常有趣的一件事情,在後續的工具配置中,我們仍然將於哈士奇見面,看看它會具體咬什麼東西!
例如當你提交了一個不符合規範的 Commit Message(此時提交失敗):
PS C:\Code\Git\algorithms> git commit -m "這是一個不符合規範的 Commit Message"
husky > commit-msg (node v12.13.1)
⧗ input: 這是一個不符合規範的 Commit Message
✖ subject may not be empty [subject-empty]
✖ type may not be empty [type-empty]
✖ found 2 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
husky > commit-msg hook failed (add --no-verify to bypass)溫馨提示:如果不知道什麼是 CLI (命令行接口),可查看 使用 NPM 發布和使用 CLI 工具[30]。
TypeScriptTypeScript 背景工具函數庫的實現採用 TypeScript,除了可以自動生成 ts 聲明文件供外部更好的提示使用之外,也可以避免 JavaScript 動態性所帶來的一些無法預料的錯誤信息(具體可查看 Top 10 JavaScript errors from 1000+ projects (and how to avoid them)[31]),從而使算法的設計更加嚴謹。TypeScript 的構建方式有很多種,除了原生編譯器 tsc 以外,還包括 Webpack、Rollup、Babel 以及 Gulp 等(更多構建工具的集成可查看 Integrating with Build Tools[32]):
Webpack 主要用於頁面應用的模塊化構建,使用 Webpack 構建會增加構建庫的體積,因此簡單工具庫的製作使用 Webpack 完全是 "殺雞用牛刀"。Rollup 是一個構建工具庫非常不錯的輕量選擇,它持有的 Tree Shaking[33] 以及構建 ES Module[34] 的特性使得它被 tsdx、microbundle 甚至 Vue 等廣泛使用。Babel 對於 TypeScript 可使用 @babel/preset-typescript[35] 去除 TypeScript 類型標記,但是不做類型編譯檢查,更多關於 Babel 對於 TypeScript 支持的限制可查看 @babel/plugin-transform-typescript - Caveats[36] 或 Babel 7 or TypeScript[37]。Gulp 是一個非常輕量的構建工具,並且也是 TypeScript 官方推薦的構建工具,具體可查看 TypeScript - Building[38],簡單的 Gulp 配置可查看 TypeScript 中文網 - Gulp[39]。由於算法的函數工具庫功能非常單一簡單,因此採用 TypeScript 官方推薦的 Gulp 工具進行構建即可滿足需求。
溫馨提示:更多構建工具可以了解 esbuild[40]、parcel[41]以及 backpack[42] 等。當然如果你想要更多了解這些構建工具的差異以及在什麼項目環境下應該做如何選型,可以自行搜索前端構建工具的對比或差異,這裡推薦一篇個人覺得總結不錯的文章 前端構建:3 類 13 種熱門工具的選型參考[43]。
TypeScript 配置本項目會構建輸出 CommonJS 工具包(npm 包)供外部使用,採用 TypeScript 設計並輸出聲明文件有助於外部更好的使用該資源包進行 API 的提示。TypeScript 編譯採用官方文檔推薦的 Gulp 工具並配合 gulp-typescript[44] 和 tsconfig.json[45] 配置文件。在根目錄下新建 tsconfig.json 文件並新增以下配置:
{
"compilerOptions": {
// 指定 ECMAScript 目標版本 "ES3"(默認), "ES5", "ES6" / "ES2015", "ES2016", "ES2017" 或 "ESNext"。
"target": "ES5",
// 構建的目標代碼刪除所有注釋,除了以 /!* 開頭的版權資訊
"removeComments": true,
// 可配合 gulp-typescript 生成相應的 .d.ts 文件
"declaration": true,
// 啟用所有嚴格類型檢查選項。啟用 --strict 相當於啟用 --noImplicitAny, --noImplicitThis, --alwaysStrict, --strictNullChecks, --strictFunctionTypes 和 --strictPropertyInitialization
"strict": true,
// 禁止對同一個文件的不一致的引用
"forceConsistentCasingInFileNames": true,
// 報錯時不生成輸出文件
"noEmitOnError": true
}
}溫馨提示:這裡沒有新增 module 配置信息,因為默認輸出 CommonJS 規範,更多關於 TypeScript 配置信息可查看TypeScript 官方文檔 / 編譯選項[46]。如果對於 CommonJS 和 ES modules 的區別不是很清晰,這裡有一些非常好的文檔可以供大家閱讀:ES modules: A cartoon deep-dive[47]、ES6 modules[48]。如果想了解如何對外提供 ES Module 可以查看 pkg.module[49]。
同時在根目錄下新建 gulpfile.js 文件:
const gulp = require("gulp");
const ts = require("gulp-typescript");
const tsProject = ts.createProject("tsconfig.json");
// 輸出 CommonJS 規範到 dist 目錄下
gulp.task("default", function () {
const tsResult = tsProject.src().pipe(tsProject());
return tsResult.js.pipe(gulp.dest("dist"));
});在 package.json 中新增 npm script 腳本:
"scripts": {
"build": "rimraf dist && gulp"
},其中 rimfaf[50] 用於在構建之前清除 dist 目錄文件內容。此時在 src 目錄下新增 TypeScript 源碼並使用 npm run build 命令可以進行項目構建並輸出 CommonJS 規範的目標代碼到 dist 目錄下。
除此之外,此項目希望可以快速生成聲明文件供外部進行代碼提示,此時仍然可以藉助 gulp-typescript 工具自動生成聲明文件。在 gulpfile.js 中新增以下配置
const gulp = require("gulp");
const ts = require("gulp-typescript");
const tsProject = ts.createProject("tsconfig.json");
const merge = require("merge2");
// 輸出 CommonJS 規範到 dist 目錄下
gulp.task("default", function () {
const tsResult = tsProject.src().pipe(tsProject());
return merge([
tsResult.dts.pipe(gulp.dest("types")),
tsResult.js.pipe(gulp.dest("dist")),
]);
});修改 build 命令使其在構建之前同時可以刪除 types 目錄:
"scripts": {
"build": "rimraf dist types && gulp",
},再次執行 npm run build 會在項目根目錄下生成 types 文件夾,該文件夾主要存放自動生成的 TypeScript 聲明文件。
需要注意發布 npm 包時默認會將當前項目的所有文件進行發布處理,但這裡希望發布的包只包含使用者需要的編譯文件 dist 和 types,因此可以通過package.json 中的 `files`[51](用於指定發布的 npm 包包含哪些文件) 欄位信息進行控制:
"files": [
"dist",
"types"
],溫馨提示:發布的 npm 包中某些文件將忽視 files 欄位信息的配置,包括 package.json、LICENSE、README.md 等。
除此之外,如果希望發布的 npm 包通過 require('algorithms-utils') 或 import 形式引入時指向 dist/index.js 文件,需要配置 package.json 中的 `main`[52] 欄位信息:
"main": "dist/index.js"溫馨提示:對於工具包使用全量引入的方式並不是一個好的選擇,可以通過具體的工具方法進行按需引入。
ESLintESLint 背景TypeScript 的代碼檢查工具主要有 TSLint 和 ESLint 兩種。早期的 TypeScript 項目一般採用 TSLint 進行檢查。TSLint 和 TypeScript 採用同樣的 AST 格式進行編譯,但主要問題是對於 JavaScript 生態的項目支持不夠友好,因此 TypeScript 團隊在 2019 年宣布全面轉向 ESLint(具體可查看 TypeScript 官方倉庫的 `.eslintrc.json`[53] 配置),更多關於轉向 ESLint 的原因可查看:
https://medium.com/palantir/tslint-in-2019-1a144c2317a9https://github.com/microsoft/TypeScript/issues/30553TypeScript 和 ESLint 使用不同的 AST 進行解析,因此為了在 ESLint 中支持 TypeScript 代碼檢查需要製作額外的自定義解析器[54](Custom Parsers,ESLint 的自定義解析器功能需要基於 ESTree[55]),目的是為了能夠解析 TypeScript 語法並轉成與 ESLint 兼容的 AST。@typescript-eslint/parser[56] 在這樣的背景下誕生,它會處理所有 ESLint 特定的配置並調用 @typescript-eslint/typescript-estree[57] 生成 ESTree-compatible AST(需要注意不僅僅兼容 ESLint,也能兼容 Prettier)。
@typescript-eslint 是一個採用 Lerna[58] 進行設計的 Monorepo 結構倉庫,除了上述提到的 npm 包之外,還包含以下兩個重要的 npm 包:
@typescript-eslint/eslint-plugin[59]: 配合 @typescript-eslint/parser 一起使用的 ESLint 插件,可以設置 TypeScript 的校驗規則。@typescript-eslint/eslint-plugin-tslint[60]: TSLint 向 ESLint 進行遷移的插件。溫馨提示:如果你正在使用 TSLint,並且你希望兼容 ESLint 或者向 ESLint 進行過渡(TSLint 和 ESLint 並存), 可查看 Migrating from TSLint to ESLint[61]。除此之外,以上所介紹的這些包發布時版本一致(為了聯合使用的兼容性),需要額外注意@typescript-eslint 對於 TypeScript 和 ESLint 的版本支持性,更多可查看 @typescript-eslint/parser 的倉庫信息。
ESLint 配置從背景的介紹中可以理解,對於全新的 TypeScript 項目(直接拋棄 TSLint)需要包含解析 AST 的解析器 @typescript-eslint/parser 和使用校驗規則的插件 @typescript-eslint/eslint-plugin,這裡需要在項目中進行安裝:
npm i --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin在根目錄新建 .eslintrc.js 配置文件,並設置以下配置:
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
};其中:
parser: '@typescript-eslint/parser':使用 ESLint 解析 TypeScript 語法plugins: ['@typescript-eslint']:在 ESLint 中加載插件 @typescript-eslint/eslint-plugin,該插件可用於配置 TypeScript 校驗規則。extends: [ ... ]:在 ESLint 中使用共享規則配置[62],其中 eslint:recommended 是 ESLint 內置的推薦校驗規則配置(也被稱作最佳規則實踐),plugin:@typescript-eslint/recommended 是類似於 eslint:recommended 的 TypeScript 推薦校驗規則配置。溫馨提示:如果你稍微閱讀一下 recommanded 源碼你會發現,其實內部可以理解為推薦校驗規則的集合。因此如果想基於 @typescript-eslint/eslint-plugin 進行自定義規則,則可參考 TypeScript Supported Rules[63]。
配置完成後在 package.json 中設置校驗命令
"scripts": {
"lint": "eslint src",
}此時如果在 src 目錄下書寫錯誤的語法,執行 npm run lint 就會輸出錯誤信息:
> 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)溫馨提示:輸出的錯誤信息是通過 ESLint Formatters[64] 生成,查看 ESLint 原始碼並調試可發現默認採用的是 stylish formatter[65] 。
ESLint 插件如果不使用插件,很難發現代碼可能存在 TypeScript 格式錯誤,因為在書寫代碼的時候除了手動執行 npm run lint 以外沒有任何的實時提示信息(你當然也可以通過 gulp監聽文件的變化並執行 npm run lint)。為了可以實時看到 TypeScript 錯誤信息,可以通過 VS Code 插件進行處理。安裝 ESLint 插件後可進行代碼的實時提示,具體如下圖所示:
當然為了防止不需要被校驗的文件出現校驗信息,可以通過 .eslintignore 文件進行配置(例如以下都是一些不需要格式校驗的配置文件):
# gulp
gulpfile.js
# eslint
.eslintrc.js
# commitizen
commitlint.config.js
# jest
jest.config.js
# build
dist此時可以發現之前執行 lint 命令的錯誤通過插件的形式可實時在 VS Code 編輯器中進行顯示。除此之外,一些 ESLint 的格式校驗錯誤(例如多餘的; 等)可通過配置 Save Auto Fix 進行保存自動格式化處理。具體 VS Code 的配置可參考 ESLint 插件[66] 的文檔說明,這邊應該需要進行如下配置:
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.fixAll.eslint": true
}溫馨提示:VS Code 的配置分為兩種類型(用戶和工作區),針對上述通用的配置主要放在用戶裡,針對不同項目的不同配置則可以放入工作區進行處理。
ESLint 確保構建VS Code 插件並不能確保代碼上傳或構建前無任何錯誤信息,此時仍然需要額外的流程能夠避免錯誤。在構建前進行 ESLint 校驗能夠確保構建時無任何錯誤信息,一旦 ESLint 校驗不通過則不允許進行源碼的構建操作:
"scripts": {
"lint": "eslint src --max-warnings 0",
"build": "npm run lint && rimraf dist types && gulp",
}需要注意在構建時進行校驗的嚴格控制,一旦 lint 拋出 warning 或者 error 則立馬終止構建(詳情可查看 ESLint 退出代碼[67])。
溫馨提示:需要注意 Shell 中的 && 和 & 是有差異的,&& 主要用於繼發執行,只有前一個任務執行成功,才會執行下一個任務,& 主要用於並發執行,表示兩個腳本同時執行。這裡構建的命令需要等待 lint 命令執行通過才能進行,一旦 lint 失敗那麼構建命令將不再執行。
ESLint 確保代碼上傳儘管可能配置了 ESLint 的校驗腳本 以及 VS Code 插件,但是有些 ESLint 的規則校驗是無法通過 Save Auto Fix 進行格式化修復的(例如質量規則),因此還需要一層保障能夠確保代碼提交之前所有的代碼能夠通過 ESLint 校驗,這個配置將在 Lint Staged 中進行講解。
PrettierPrettier 背景Prettier 是一個統一代碼格式風格的工具,如果你不清楚為什麼需要使用 Prettier,可以查看 Why Prettier?[68]。很多人可能疑惑,ESLint 已經能夠規範我們的代碼風格,為什麼還需要 Prettier?在 Prettier vs Linters[69] 中詳細說明了兩者的區別,Linters 有兩種類型的規則:
格式規則(Formatting rules):例如 max-len[70]、keyword-spacing[71] 以及 no-mixed-spaces-and-tabs[72] 等質量規則(Code-quality rules):例如 no-unused-vars[73]、no-implicit-globals[74] 以及 prefer-promise-reject-errors[75] 等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[76]。
溫馨提示:在 VS Code 中使用 ESLint 匹配到相應的規則時會產生黃色波浪線以及紅色文件名進行錯誤提醒。Prettier 更希望你對格式規則無感知,從而不會讓你覺得有任何使用的負擔。如果想要了解更多 Prettier,還可以查看 Prettier 的背後思想 Option Philosophy[77],個人認為了解一個產品設計的哲學能更好的指導你使用該產品。
Prettier 配置首先安裝 Prettier 所需要的依賴:
npm i prettier eslint-config-prettier --save-dev其中:
eslint-config-prettier[78]: 用於解決 ESLint 和 Prettier 配合使用時容易產生的格式規則衝突問題,其作用就是關閉 ESLint 中配置的一些格式規則,除此之外還包括關閉 @typescript-eslint/eslint-plugin、eslint-plugin-babel、eslint-plugin-react、eslint-plugin-vue、eslint-plugin-standard 等格式規則。理論上而言,在項目中開啟 ESLint 的 extends 中設置的帶有格式規則校驗的規則集,那麼就需要通過 eslint-config-prettier 插件關閉可能產生衝突的格式規則:
{
"extends": [
"plugin:@typescript-eslint/recommended",
// 用於關閉 ESLint 相關的格式規則集,具體可查看 https://github.com/prettier/eslint-config-prettier/blob/master/index.js
"prettier",
// 用於關閉 @typescript-eslint/eslint-plugin 插件相關的格式規則集,具體可查看 https://github.com/prettier/eslint-config-prettier/blob/master/%40typescript-eslint.js
"prettier/@typescript-eslint",
]
}配置完成後,可以通過命令行接口[79]運行 Prettier:
"scripts": {
"prettier": "prettier src test --write",
},--write 參數類似於 ESLint 中的 --fix(在 ESLint 中使用該參數還是需要謹慎哈,建議還是使用 VS Code 的 Save Auto Fix 功能),主要用於自動修復格式錯誤。此時書寫格式的錯誤代碼:
import great from "@/greet";
// 中間這麼多空行
export default {
great,
};執行 npm run prettier 進行格式修復:
PS C:\Code\Git\algorithms> npm run prettier
> algorithms-utils@1.0.0 prettier C:\Code\Git\algorithms
> prettier src test --write
src\greet.ts 149ms
src\index.ts 5ms
test\greet.spec.ts 11ms修復之後的的文件格式如下:
import great from "@/greet";
export default {
great,
};需要注意如果某些規則集沒有對應的 eslint-config-prettier 關閉配置,那麼可以先通過 CLI helper tool[80] 檢測是否有重複的格式規則集在生效,然後可以通過手動配置 eslintrc.js 的形式進行關閉:
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此時假設 eslint-config-prettier 沒有類似的關閉格式規則集(例如本項目中配置的 plugin:jest/recommended 可能存在規則衝突),那麼可以通過配置 .eslintrc.js 的形式自己手動關閉相應衝突的格式規則。
溫馨提示:ESLint 可以對不同的文件支持不同的規則校驗, 因此 --print-config 只能對應單個文件的衝突格式規則檢查。由於通常的項目是一套規則對應一整個項目,因此對於整個項目所有的規則只需要校驗一個文件是否有格式規則衝突即可。
Prettier 插件通過命令行接口 --write 的形式可以進行格式自動修復,但是類似 ESLint,我們更希望項目在實時編輯時可以通過保存就能自動格式化代碼(鬼知道 --fix 以及 --write 格式了什麼文件,當然更希望通過肉眼的形式立即感知代碼的格式化變化),此時可以通過配置 VS Code 的 Prettier - Code formatter[81] 插件進行 Save Auto Fix,具體的配置查看插件文檔。
Prettier 確保代碼上傳和 ESLint 一樣,儘管可能配置了 Prettier 的自動修復格式腳本以及 VS Code 插件,但是無法確保格式遺漏的情況,因此還需要一層保障能夠確保代碼提交之前能夠進行 Prettier 格式化,這個配置將在 Lint Staged 中講解,更多配置方案也可以查看 Prettier - Pre-commit Hook[82]。
Lint StagedLint Staged 背景在 Git Commit Message 中使用了 commitlint[83] 工具配合 husky 可以防止生成不規範的 Git Commit Message,從而阻止用戶進行不規範的 Git 代碼提交,其原理就是監聽了 Git Hook 的執行腳本(會在特定的 Git 執行命令諸如 commit、push、merge 等觸發之前或之後執行相應的腳本鉤子)。Git Hook 其實是進行項目約束非常好用的工具,它的作用包括但不限於:
Git Commit Message 規範強制統一Prettier 自動格式化(類似的還包括 Style 樣式格式等)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 Hook 信息可以查看 Git Hook 官方文檔[84] 或 Git 鉤子:自定義你的工作流[85]。
需要注意初始化 Git 之後默認會在 .git/hooks 目錄下生成所有 Git 鉤子的 Shell 示例腳本,這些腳本是可以被定製化的。對於前端開發而言去更改這些示例腳本適配前端項目非常不友好(大多數前端開發同學壓根不會設計 Shell 腳本,儘管這個對於製作工具是一件非常高效的事情),因此社區就出現了類似的增強工具,它們對外拋出的是簡單的鉤子配置(例如 ghooks[86] 在 package.json 中只需要進行簡單的鉤子屬性配置[87]),而在內部則通過替換 Git 鉤子示例腳本的形式使得外部配置的鉤子可以被執行,例如 husky[88]、ghooks 以及 pre-commit[89] 等。
溫馨提示:Git Hook 還可以定製腳本執行的語言環境,例如對於前端而言當然希望使用熟悉的 Node 進行腳本設計,此時可以通過在腳本文件的頭部設置 #! /usr/bin/env node 將 Node 作為可執行文件的環境解釋器,如果你之前看過 使用 NPM 發布和使用 CLI 工具[90] 可能會對這個環境解析器相對熟悉,這裡也給出一個使用 Node 解釋器的示例:ghooks - hook.template.raw[91],ghooks 的實現非常簡單,感興趣的同學可以仔細閱讀一些源碼的實現。
介紹 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[92] 工具可以防止生成不規範的 Git Commit Message,從而阻止用戶進行 Git 代碼提交。但是如果想要防止團隊協作時開發者提交不符合 ESLint 規則的代碼則可以通過 lint-staged[93] 工具來實現。lint-staged 可以在用戶提交代碼之前(生成 Git Commit Message 信息之前)使用 ESLint 檢查 Git 暫存區中的代碼信息(git add 之後的修改代碼),一旦存在 💩 一樣不符合校驗規則的代碼,則可以終止提交行為。需要注意的是 lint-staged 不會檢查項目的全量代碼(全量使用 ESLint 校驗對於較大的項目可能會是一個相對耗時的過程),而只會檢查添加到 Git 暫存區中的代碼。根據官方文檔執行以下命令自動生成配置項信息:
npx mrm lint-staged需要注意默認生成的配置文件是針對 JavaScript 環境的,手動修改 package.json 中的配置信息進行 TypeScript 適配:
// 我們的哈士奇再次上場,這次它是要咬著你的 ESLint 不放了,這裡我簡稱它的動作為 "咬 💩" ~~~
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
// 這裡需要注意 ESLint 腳本的 --max-warnings 0
// 否則就算存在 warning 也不會終止提交行為
// 這裡追加了 Prettier 的自動格式化,確保代碼提交之前所有的格式能夠修復
"*.ts": ["npm run lint", "npm run prettier"]
}此時如果將要提交的代碼有 💩 , 則提交時會提示錯誤信息且提交會被強制終止:
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 在 package.json 中配置了 pre-commit 和 commit-msg 兩個 Git 鉤子[94],優先使用 pre-commit 鉤子執行 ESLint 校驗,如果校驗失敗則終止運行。如果校驗成功則會繼續執行 commit-msg 校驗 Git Commit Message,例如以下是 ESLint 校驗通過但是 Commit Message 校驗失敗的例子:
PS C:\Code\Git\algorithms> git commit -m "這是一個不符合規範的 Commit Message"
// pre-commit 鉤子 ESLint 校驗通過
husky > pre-commit (node v12.13.1)
[STARTED] Preparing...
[SUCCESS] Preparing...
[STARTED] Running tasks...
[STARTED] Running tasks for *.ts
[STARTED] npm run lint-strict
[SUCCESS] npm run lint-strict
[SUCCESS] Running tasks for *.ts
[SUCCESS] Running tasks...
[STARTED] Applying modifications...
[SUCCESS] Applying modifications...
[STARTED] Cleaning up...
[SUCCESS] Cleaning up...
// commit-msg 鉤子 Git Commit Message 校驗失敗
husky > commit-msg (node v12.13.1)
⧗ input: 這是一個不符合規範的 Commit Message
✖ subject may not be empty [subject-empty]
✖ type may not be empty [type-empty]
✖ found 2 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
husky > commit-msg hook failed (add --no-verify to bypass)
Jest測試背景如果對於測試的概念和框架不是特別清楚,這裡推薦一些可查看的文章:
JavaScript 程序測試[95] - 全面的測試基礎知識New to front-end testing? Start from the top of the pyramid![96] - 重點可以了解一下測試金字塔和測試置信度[譯] JavaScript 單元測試框架:Jasmine, Mocha, AVA, Tape 和 Jest 的比較[97] - 單元測試框架對比中文版(2018)JavaScript unit testing frameworks in 2020: A comparison[98] - 單元測試框架對比英文版(2020)除此之外,如果想了解一些額外的測試技巧,這裡推薦一些社區的最佳實踐:
javascript-testing-best-practices[99]ui-testing-best-practices[100]由於這裡只是 Node 環境工具庫包的單元測試,在對比了各個測試框架之後決定採用 Jest[101] 進行單元測試:
內置斷言庫可實現開箱即用(從 it 到 expect, Jest 將整個工具包放在一個地方)Jest 可以可靠地並行運行測試,並且為了讓加速測試進程,Jest 會優先運行之前失敗的測試用例溫馨提示:前端測試框架很多,相比簡單的單元測試,e2e 測試會更複雜一些(不管是測試框架的支持以及測試用例的設計)。之前使用過 Karma 測試管理工具配合 Mocha 進行瀏覽器環境測試,也使用過 PhantomJS 以及 Nightwatch(使用的都是皮毛),印象最深刻的是使用 testcafe[102] 測試框架(複雜的 API 官方文檔),除此之外如果還感興趣,也可以了解一下 cypress[103] 測試框架。
Jest 配置本項目的單元測試主要採用了 Jest[104] 測試框架。Jest 如果需要對 TypeScript 進行支持,可以通過配合 Babel 的形式,具體可查看 Jest - Using TypeScript[105],但是採用 Babel 會產生一些限制(具體可查看 Babel 7 or TypeScript[106])。由於本項目沒有採用 Babel 進行轉譯,並且希望能夠完美支持類型檢查,因此採用 ts-jest[107] 進行單元測試。按照官方教程進行依賴安裝和項目初始化:
npm install --save-dev jest typescript ts-jest @types/jest
npx ts-jest config:init子啊根目錄的 ject.config.js 文件中進行 Jest 配置修改:
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
// 輸出覆蓋信息文件的目錄
coverageDirectory: "./coverage/",
// 覆蓋信息的忽略文件模式
testPathIgnorePatterns: ["<rootDir>/node_modules/"],
// 如果測試覆蓋率未達到 100%,則測試失敗
// 這裡可用於預防代碼構建和提交
coverageThreshold: {
global: {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
},
// 路徑映射配置,具體可查看 https://kulshekhar.github.io/ts-jest/user/config/#paths-mapping
// 需要配合 TypeScript 路徑映射,具體可查看:https://www.tslang.cn/docs/handbook/module-resolution.html
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1",
},
};需要注意路徑映射也需要配置 tsconfig.json 中的 paths 信息,同時注意將測試代碼包含到 TypeScript 的編譯目錄中。配置完成後在 package.json 中配置測試命令:
"scripts": {
"lint": "eslint src --max-warnings 0",
"test": "jest --bail --coverage",
"build": "npm run lint && npm run jest && rimraf dist types && gulp",
}需要注意 Jest 中的這些配置信息(更多配置信息可查看 Jest CLI Options[108]):
bail 的配置作用相對類似於 ESLint 中的 max-warnings,設置為 true 則表明一旦發現單元測試用例錯誤則停止運行其餘測試用例,從而可以防止運行用例過多時需要一直等待用例全部運行完畢的情況。coverage 主要用於在當前根目錄下生成 coverage 代碼的測試覆蓋率報告,該報告還可以上傳 coveralls[109] 進行 Github 項目的 Badges 顯示。溫馨提示:Jest CLI Options 中的 findRelatedTests 可用於配合 pre-commit 鉤子去運行最少量的單元測試用例,可配合 lint-staged 實現類似於 ESLint 的作用,更多細節可查看 `lint-staged - Use environment variables with linting commands`[110]。
在當前根目錄的 test 目錄下新建 greet.spec.ts 文件,並設計以下測試代碼:
import greet from "@/greet";
describe("src/greet.ts", () => {
it("name param test", () => {
expect(greet("world")).toBe("Hello from world 1");
});
});溫馨提示:測試文件有兩种放置風格,一種是新建 test 文件夾,然後將所有的測試代碼集中在 test 目錄下進行管理,另外一種是在各個源碼文件的同級目錄下新建 __test__ 目錄,進行就近測試。大部分的項目可能都會傾向於採用第一種目錄結構(可以隨便找一些 github 上的開源項目進行查看,這裡 ts-test 則是採用了第二種測試結構)。除此之外,需要注意 Jest 通過配置 `testMatch`[111] 或 `testRegex`[112] 可以使得項目識別特定格式文件作為測試文件進行運行(本項目採用默認配置可識別後綴為 .spec 的文件進行單元測試)。
Jest 確保構建單獨通過執行 npm run test 命令進行單元測試,這裡演示執行構建命令時的單元測試(需要保證構建之前所有的單元測試用例都能通過)。如果測試失敗,那麼應該防止繼續構建,例如進行失敗的構建行為:
PS C:\Code\Git\algorithms> npm run build
> algorithms-utils@1.0.0 build C:\Code\Git\algorithms
> npm run lint-strict && npm run jest && rimraf dist types && gulp
> algorithms-utils@1.0.0 lint-strict C:\Code\Git\algorithms
> eslint src --max-warnings 0
> algorithms-utils@1.0.0 jest C:\Code\Git\algorithms
> jest --coverage
PASS dist/test/greet.spec.js
FAIL test/greet.spec.ts
● 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)
|----||----|----|----
| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
| | ---- | | ---- | ---- | ---- |
| All files | 100 | 100 | 100 | 100 |
| greet.ts | 100 | 100 | 100 | 100 |
| | ---- | | ---- | ---- | ---- |
Test Suites: 1 failed, 1 passed, 2 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 3.45 s
Ran all test suites.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! algorithms-utils@1.0.0 jest: `jest --coverage`
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-12T13_42_11_628Z-debug.log
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! algorithms-utils@1.0.0 build: `npm run lint-strict && npm run jest && rimraf dist types && gulp`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the algorithms-utils@1.0.0 build 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-12T13_42_11_673Z-debug.log需要注意由於是並行(&&)執行腳本,因此執行構建命令時(npm run build)會先執行 ESLint 校驗,如果 ESLint 校驗失敗那麼退出構建,否則繼續進行 Jest 單元測試。如果單元測試失敗那麼退出構建,只有當兩者都通過時才會進行源碼構建。
Jest 確保代碼上傳除了預防不負責任的代碼構建以外,還需要預防不負責任的代碼提交。配合 lint-staged 可以防止未跑通單元測試的代碼進行遠程提交:
"scripts": {
"lint": "eslint src --max-warnings 0",
"test": "jest --bail --coverage",
},
"lint-staged": {
"*.ts": [
"npm run lint",
"npm run test"
]
}此時如果單元測試有誤,都會停止代碼提交:
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溫馨提示:想要了解更多關於 Jest 的生態可以查看 awesome-jest[113]。
Jest 對於 ESLint 支持src 目錄下的源碼通過配置 @typescript-eslint/eslint-plugin 可進行推薦規則的 ESLint 校驗,為了使得 test 目錄下的測試代碼能夠進行符合 Jest 推薦規則的 ESLint 校驗,可以通過配置 eslint-plugin-jest[114] 進行支持(ts-jest 項目就是採用了該插件進行 ESLint 校驗,具體可查看配置文件 `ts-jest/.eslintrc.js`[115]),這裡仍然採用推薦的規則配置:
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
// 新增推薦的 ESLint 校驗規則
// 所有規則集查看:https://github.com/jest-community/eslint-plugin-jest#rules(recommended 標識表明是推薦規則)
"plugin:jest/recommended",
],
};為了驗證推薦規則是否生效,這裡可以找一個 `no-identical-title`[116] 規則進行驗證:
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此時會發現 ESLint 拋出了相應的錯誤信息。需要注意採用此 ESLint 校驗之後也會在 VS Code 中實時生成錯誤提示(相應的代碼下會有紅色波浪線,滑鼠移入後會產生 Tooltip 提示該錯誤的相應規則信息,除此之外當前工程目錄下對應的文件名也會變成紅色),此後的 Git 提交以及 Build 構建都會失敗!
溫馨提示:如果你希望 Jest 測試的代碼需要一些格式規範,那麼可以查看 eslint-plugin-jest-formatting[117] 插件。
Npm Script Hook當你查看前端開源項目時第一時間可能會找 package.json 中的 main、bin 以及 files 等欄位信息,除此之外如果還想深入了解項目的結構,可能還會查看 scripts 腳本欄位信息用於了解項目的開發、構建、測試以及安裝等流程。npm 的腳本功能非常強大,你可以利用腳本製作項目需要的任何流程工具。本文不會過多介紹 npm 腳本的功能,只是講解一下其中用到的 鉤子[118] 功能。
目前在本項目中使用的一些腳本命令如下(就目前而言腳本相對較少,定義還蠻清晰的):
"lint": "eslint src test --max-warnings 0",
"test": "jest --bail --coverage",
"build": "npm run lint && npm run prettier && npm run test && rimraf dist types && gulp",
"changelog": "rimraf CHANGELOG.md && conventional-changelog -p angular -i CHANGELOG.md -s"重點看下 build 腳本命令,會發現這個腳本命令包含了大量的繼發執行腳本,但真正和 build 相關的只有 rimraf dist types && gulp 這兩個腳本。這裡通過 npm 的腳本鉤子 pre 和 post 將腳本的功能區分開,從而使腳本的語義更加清晰(當然腳本越來越多的時候也可能容易增加開發者的認知負擔)。npm 除了指定一些特殊的腳本鉤子以外(例如 prepublish、postpublish、preinstall、postinstall等),還可以對任意腳本增加 pre 和 post 鉤子,這裡通過自定義鉤子將並發執行的腳本進行簡化:
"lint": "eslint src test --max-warnings 0",
"test": "jest --bail --coverage",
"prebuild": "npm run lint && npm run prettier && npm run test",
"build": "rimraf dist types && gulp",
"changelog": "rimraf CHANGELOG.md && conventional-changelog -p angular -i CHANGELOG.md -s"此時如果執行 npm run build 命令時事實上類似於執行了以下命令:
npm run prebuild && npm run build之後設計的腳本如果繼發執行繁多,那麼都會採用 npm scripts hook 進行設計。
溫馨提示:大家可能會奇怪什麼地方需要類似於 preinstall 或 preuninstall 這樣的鉤子,例如查看 husky - package.json[119],husky 在安裝的時候因為要植入 Git Hook 腳本從而帶來了一些副作用(此時當然可以通過 preinstall 觸發 Git Hook 腳本植入的邏輯)。如果不想使用 husky,那麼卸載後需要清除植入的腳本從而不妨礙原有的 Git Hook 功能。當然如果想要了解更多關於 npm 腳本的信息,可以查看 npm-scripts[120] 或 npm scripts 使用指南[121]。
VuepressVuepress 背景一般組件庫或工具庫都需要設計一個演示文檔(提供良好的開發體驗)。一般的工具庫可以採用 tsdoc[122]、jsdoc[123] 或 esdoc[124] 等工具進行 API 文檔的自動生成,但往往需要符合一些注釋規範,這些注釋規範在某種程度上可能會帶來開發負擔,當然也可以交給 VS Code 的插件進行一鍵生成,例如 Document This For jsdoc[125] 或 TSDoc Comment[126]。
組件庫 Element UI 採用 vue-markdown-loader[127](Convert Markdown file to Vue Component using markdown-it) 進行組件的 Demo 演示設計,但是配置相對複雜。更簡單的方式是配合 Vuepress[128] 進行設計,它的功能非常強大,但前提是熟悉 Vue,因為可以在 Markdown 中使用 Vue 語法。當然如果是 React 組件庫的 Demo 演示,則可以採用 dumi[129] 生成組件 Demo 演示文檔(不知道沒有更加好用的類 Vuepress 的 React 組件文檔生成器, 更多和 React 文檔相關也可以了解 react-markdown[130]、react-static[131] 等)。
由於之前採用過 Vuepress 設計 Vue 組件庫的 Demo 演示文檔,因此這裡仍然沿用它來設計工具庫包的 API 文檔(如果你想自動生成 API 文檔,也可以額外配合 tsdoc 工具)。採用 Vuepress 設計文檔的主要特點如下:
可以在 Markdown 中直接使用 Vue(還可以自定義 Vue 文檔視圖組件)可以安裝 Vuepress 插件(後續需要支持的 Latex[132] 排版就可以利用現有的插件能力生成)Vuepress 配置先按照官方的 快速上手[133] 文檔進行依賴安裝和 npm scripts 腳本設置:
"scripts": {
"docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs"
}按照 Vuepress 官網約定優於配置的原則進行演示文檔的目錄結構[134]設計,官方的文檔可能一下子難以理解,可以先設計一個最簡單的目錄:
.
├── docs
│ ├── .vuepress
│ │ └── config.js # 配置文件
│ └── README.md # 文檔首頁
└── package.json根據默認主題 / 首頁[135]在 docs/README.md 進行首頁設計:
---
home: true
# heroImage: /hero.png
heroText: algorithms-utils
tagline: 算法與 TypeScript 實現
actionText: 開始學習
actionLink: /guide/
features:
- title: 精簡理論
details: 精簡《算法導論》的內容,幫助自己更容易學習算法理論知識。
- title: 習題練習
details: 解答《算法導論》的習題,幫助自己更好的實踐算法理論知識。
- title: 面題精選
details: 搜集常見的面試題目,提升自己的算法編程能力以及面試通過率。
footer: MIT Licensed | Copyright © 2020-present 子弈
---根據配置[136] 對 docs/.vuepress/config.js 文件進行基本配置:
const packageJson = require("../../package.json");
module.exports = {
// 配置網站標題
title: packageJson.name,
// 配置網站描述
description: packageJson.description,
// 配置基本路徑
base: "/algorithms/",
// 配置基本埠
port: "8080",
};此時通過 npm run docs:dev 進行開發態文檔預覽:
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/效果如下:
當然除了以上設計的首頁,在本項目中還會設計導航欄[137]、側邊欄[138]、使用插件[139]、使用組件[140]等。這裡重點講解一下 Webpack 構建[141] 配置。
為了在 Markdown 文檔中可以使用 src 目錄的 TypeScript 代碼,這裡對 .vuepress/config.js 文件進行配置處理:
const packageJson = require("../../package.json");
const sidebar = require("./config/sidebar.js");
const nav = require("./config/nav.js");
const path = require("path");
module.exports = {
title: packageJson.name,
description: packageJson.description,
base: "/algorithms/",
port: "8080",
themeConfig: {
nav,
sidebar,
},
plugins: [
"vuepress-plugin-cat",
[
"mathjax",
{
target: "svg",
macros: {
"*": "\\times",
},
},
],
// 增加 Markdown 文檔對於 TypeScript 語法的支持
[
"vuepress-plugin-typescript",
{
tsLoaderOptions: {
// ts-loader 的所有配置項
},
},
],
],
chainWebpack: (config) => {
config.resolve.alias.set("image", path.resolve(__dirname, "public"));
// 在文檔中模擬庫包的引入方式
// 例如發布了 algorithms-utils 庫包之後,
// import greet from 'algorithms-utils/greet.ts' 在 Vuepress 演示文檔中等同於
// import greet from '~/src/greet.ts',
// 其中 ~ 在這裡只是表示項目根目錄
config.resolve.alias.set(
"algorithms-utils",
path.resolve(__dirname, "../../src")
);
},
};溫馨提示:這裡的 Webpack 配置採用了 webpack-chain[142] 鏈式操作,如果想要採用 Webpack 對象的配置方式則可以查看 Vuepress - 構建流程 - configurewebpack[143]。
此時可以在 Vuepress 的 Markdown 文檔中進行 TypeScript 引入的演示文檔設計:
# Test vuepress
::: danger 測試 Vuepress
引入 greet.ts 並進行調用測試。
:::
<template>
<collapse title="查看答案">{{msg}}</collapse>
</template>
<template>
<div>{{msg}}</div>
</template>
<script lang="ts">
import greet from 'algorithms-utils/greet'
const msg = greet('ziyi')
export default {
data() {
return {
msg
}
},
}
</script>啟動 Vuepress 查看演示文檔:
可以發現在 Markdown 中引入的 src/greet.ts 代碼生效了,最終通過 npm run docs:build 可以生成演示文檔的靜態資源進行部署和訪問。
溫馨提示:更多本項目的 Vuepress 配置信息可查看 Commit 信息,除此之外如果還想知道更多 Vuepress 的生態,例如有哪些有趣插件或主題,可查看 awesome-vuepress[144] 或 Vuepress 社區[145]。
文檔工具和規範通常在書寫文檔的時候很多同學都不注重文檔的潔癖,其實書寫文檔和書寫代碼一樣需要一些格式規範。markdownlint[146] 是類似於 ESLint 的 Markdown 格式校驗工具,通過它可以更好的規範我們書寫的文檔。當然 Markdown 的格式校驗不需要像 ESLint 或者 Prettier 校驗那樣進行強約束,簡單的能夠做到提示和 Save Auto Fix 即可。
通過安裝 Vs Code 插件 markdownlint[147] 並進行 Save Auto Fix 配置(在插件中明確列出了哪些規則是可以被 Fix 的)。安裝完成後查看剛剛進行的測試文件:
此時會發現插件生效了,但是在 Markdown 中插入 html 是必須的一個能力(Vuepress 支持的能力就是在 Markdown 中使用 Vue),因此可以通過 .markdownlintrc 文件將相應的規則屏蔽掉。
溫馨提示:如果你希望在代碼提交之前或文檔構建之前能夠進行 Markdown 格式校驗,則可以嘗試它的命令行接口 markdownlint-cli[148]。除此之外,如果對文檔的設計沒有想法或者不清楚如何書寫好的技術文檔,可以查看 技術文章的寫作技巧分享[149],一定能讓你有所收穫。
Github ActionsCI / CD 背景前提提示:個人對於 CI / CD 可能相對不夠熟悉,只是簡單的玩過 Travis、Gitlab CI / CD 以及 Jenkins。
關於 CI / CD 的背景這裡就不再過多介紹,有興趣的同學可以看看以下一些好文:
Introduction to CI/CD with GitLab(中文版)[150]當我有伺服器時我做了什麼 · 個人伺服器運維指南[153](這個系列有點佩服啊)在 Introduction to CI/CD with GitLab(中文版)[154] 中你可以清晰的了解到 CI 和 CD 的職責功能:
通過以下圖可以更清晰的發現 Gitlab 在每個階段可用的功能:
由於本項目依賴 Github,因此沒法使用 Gitlab 默認集成的能力。之前的 Github 項目採用了 Travis 進行項目的 CI / CD 集成,現在因為有了更方便的 Github Actions,因此決定採用 Github 自帶的 Actions 進行 CI / CD 能力集成(大家如果想更多了解這些 CI / CD 的差異請自行 Google 哈)。Github Actions 所帶來的好處在於:
可復用的 Actions(以前你需要寫複雜的腳本,現在可以復用別人寫好的腳本,可以簡單理解為 CI 腳本插件化)支持更多的 webhook[155],這些當然是 Github 生態特有的競爭力當然也會產生一些限制[156],這些限制主要是和執行時間以及次數相關。需要注意類似於 Jenkins 等支持本地連接運行,Github Actions 也支持連接到本地機器運行 workflow,因此部分限制可能不受本地運行的限制。
溫馨提示:本項目中使用到的 CI / CD 功能相對簡單,如果想了解更多通用的 Actions,可查看 官方 Actions[157] 和 awesome-actions[158]。最近在使用 Jenkins 做前端的自動化構建優化,後續可能會出一篇簡單的教程文章(當然會跟普通講解的用法會有所不同嘍)。
Github Actions 配置本項目的配置可能會包含以下三個方面:
這裡主要講解自動更新靜態資源流程,大致需要分為以下幾個步驟(以下都是在 Github 伺服器上進行操作,你可以理解為新的服務環境):
拉取當前 Github 倉庫代碼並切換到相應的分支通過查看 官方 Actions[159] 和 awesome-actions[160],找到所需的 Actions:
Checkout[161]: 從 Github 拉取倉庫代碼到 Github 伺服器的 $GITHUB_WORKSPACE 目錄下setup-node[163]: 安裝 Node 和 Npm 環境actions-gh-pages[164]: 在 Github 上發布靜態資源溫馨提示:可用的 Action 很多,這裡只是設置了一個簡單的流程。
在 .github/workflows 下新增 mian.yml 配置文件:
# 以下都是官方文檔的簡單翻譯
# 當前的 yml(.yaml) 文件是一個 workflow,是持續集成一次運行的一個過程,必須放置在項目的 .github/workflow 目錄下
# 如果不清楚 .yml 文件格式語法,可以查看 https://www.codeproject.com/Articles/1214409/Learn-YAML-in-five-minutes
# 初次編寫難免會產生格式問題,可以使用 VS Code 插件進行格式檢測,https://marketplace.visualstudio.com/items?itemName=OmarTawfik.github-actions-vscode
# 具體各個配置屬性可查看 https: //docs.github.com/en/actions/reference/workflow-syntax-for-github-actions
# workflow 的執行仍然會受到一些限制,例如
# - 每個 job 最多執行 6 小時(本地機器不受限制)
# - 每個 workflow 最多執行 72 小時
# - 並發 job 的數量會受到限制
# - 更多查看 https: //docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#usage-limits
# name: 當前 workflow 的名稱
name: Algorithms
# on: 指定 workflow 觸發的 event
#
# event 有以下幾種類型
# - webhook
# - scheduled
# - manual
on:
# push: 一個 webhook event,用於提交代碼時觸發 workflow,也可以是觸發列表,例如 [push, pull_request]
# workflows 觸發的 event 大部分是基於 webhook 配置,以下列舉幾個常見的 webhook:
# - delete: 刪除一個 branch 或 tag 時觸發
# - fork / watch: 某人 fork / watch 項目時觸發(你問有什麼用,發送郵件通知不香嗎?)
# - pull_request: 提交 PR 時觸發
# - page_build: 提交 Github Pages-enabled 分支代碼時觸發
# - push: 提交代碼到特定分支時觸發
# - registry_package: 發布或跟新 package 時觸發
# 更多 webhook 可查看 https: //docs.github.com/en/actions/reference/events-that-trigger-workflows
# 從這裡可以看出 Git Actions 的一大特點就是 Gihub 官方提供的一系列 webhook
push:
# branches: 指定 push 觸發的特定分支,這裡你可以通過列表的形式指定多個分支
branches:
- feat/framework
#
# branches 的指定可以是通配符類型,例如以下配置可以匹配 refs/heads/releases/10
# - 'releases/**'
#
# branches 也可以使用反向匹配,例如以下不會匹配 refs/heads/releases/10
# - '!releases/**'
#
# branches-ignore: 只對 [push, pull_request] 兩個 webhook 起作用,用於指定當前 webhook 不觸發的分支
# 需要注意在同一個 webhook 中不能和 branches 同時使用
#
# tags: 只對 [push, pull_request] 兩個 webhook 起作用,用於指定當前 webhook 觸發的 tag
#
# tags:
# - v1 # Push events to v1 tag
# - v1.* # Push events to v1.0, v1.1, and v1.9 tags
#
# tags-ignore: 類似於 branches-ignore
#
# paths、paths-ignore...
#
# 更多關於特定過濾模式可查看 https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet
#
# 其他的 webhook 控制項還包括 types(不是所有的 webhook 都有 types),例如已 issues 為例,可以在 issues 被 open、reopened、closed 等情況下觸發 workflow
# 更多 webhook 的 types 可查看 https: //docs.github.com/en/actions/reference/events-that-trigger-workflows#webhook-events
#
# on:
# issues:
# types: [opened, edited, closed]
# 除此之外如果對於每個分支有不同的 webhook 觸發,則可以通過以下形式進行多個 webhook 配置
#
# push:
# branches:
# - master
# pull_request:
# branches:
# - dev
#
# 除了以上所說的 webhook event,還有 scheduled event 和 manual event
# scheduled event: 用於定時構建,例如最小的時間間隔是 5 min 構建一次
# 具體可查看 https: //docs.github.com/en/actions/reference/events-that-trigger-workflows#scheduled-events
# env: 指定環境變量(所有的 job 生效,每一個 job 可以獨立通過 jobs.<job_id>.env、jobs.<job_id>.steps.env 配置)
# defaults / defaults.run: 所有的 job 生效,每一個 job 可以獨立通過 jobs.<job_id>.defaults 配置
# deafults
# defaults.run
# jobs: 一個 workflow 由一個或多個 job 組成
jobs:
# job id: 是 job 的唯一標識,可以通過 _ 進行連接,例如: my_first_job,例如這裡的 build 就是一個 job id
build_and_deploy:
# name: 在 Github 中顯示的 job 名稱
name: Build And Deploy
#
# needs: 用於繼發執行 job,例如當前 job build 必須在 job1 和 job2 都執行成功的基礎上執行
# needs: [job1, job2]
#
# runs-on: job 運行的環境配置,包括:
# - windows-latest
# - windows-2019
# - ubuntu-20.04
# - ubuntu-latest
# - ubuntu-18.04
# - ubuntu-16.04
# - macos-latest
# - macos-10.15
# - self-hosted(本地機器,具體可查看 https: //docs.github.com/en/actions/hosting-your-own-runners/using-self-hosted-runners-in-a-workflow)
runs-on: ubuntu-latest
#
# outputs: 用於輸出信息
#
# env: 用於設置環境變量
#
# defaults: 當前所有 step 的默認配置
#
# defaults.run
# if: 滿足條件執行當前 job
# steps: 一個 job 由多個 step 組成,step 可以
# - 執行一系列 tasks
# - 執行命令
# - 執行 action
# - 執行公共的 repository
# - 在 Docker registry 中的 action
steps:
#
# id: 類似於 job id
#
# if: 類似於 job if
#
# name: 當前 step 的名字
- name: Checkout
#
# uses: 用於執行 action
#
# action: 可以重複使用的單元代碼
# - 為了 workflow 的安全和穩定建議指定 action 的發布版本或 commit SHA
# - 使用指定 action 的 major 版本,這樣可以允許你接收 fixs 以及 安全補丁並同時保持兼容性
# - 儘量不建議使用 master 版本,因為 master 很有可能會被發布新的 major 版本從而破壞了 action 的兼容性
# - action 可能是 JavaScript 文件或 Docker 容器,如果是 Docker 容器,那麼 runs-on 必須指定 Linux 環境
#
# 指定固定 commit SHA
# uses: actions/setup-node@74bc508
# 指定一個 major 發布版本
# uses: actions/setup-node@v1
# 指定一個 minor 發布版本
# uses: actions/setup-node@v1.2
# 指定一個分支
# uses: actions/setup-node@master
# 指定一個 Github 倉庫子目錄的特定分支、ref 或 SHA
# uses: actions/aws/ec2@master
# 指定當前倉庫所在 workflows 的目錄地址
# uses: ./.github/actions/my-action
# 指定在 Dock Hub 發布的 Docker 鏡像地址
# uses: docker: //alpine: 3.8
# A Docker image in a public registry
# uses: docker: //gcr.io/cloud-builders/gradle
# checkout action 主要用於向 github 倉庫拉取原始碼(需要注意 workflow 是運行在伺服器上,因此需要向當前 github 拉取倉庫原始碼)
# 它的功能包括但不限於
# - Fetch all history for all tags and branches
# - Checkout a different branch
# - Checkout HEAD^
# - Checkout multiple repos (side by side)
# - Checkout multiple repos (nested)
# - Checkout multiple repos (private)
# - Checkout pull request HEAD commit instead of merge commit
# - Checkout pull request on closed event
# - Push a commit using the built-in token
# checkout action: https: //github.com/actions/checkout
uses: actions/checkout@v2
# with: action 提供的輸入參數
with:
# 指定 checkout 的分支、tag 或 SHA
# 更多 checkout action 的配置可查看 https: //github.com/actions/checkout#usage
ref: feat/ci
# args: 用於 Docker 容器的 CMD 指令參數
# entrypoint: Docker 容器 action(覆蓋 Dockerfile 的 ENTRYPOINT) 和 JavaScript action 都可以使用
#
# run: 使用當前的作業系統的默認的 non-login shell 執行命令行程序
# 運行單個腳本
# run: npm install
# 運行多個腳本
# run: |
# npm ci
# npm run build
#
# working-directory: 用於指定當前腳本運行的目錄
# working-directory: ./temp
#
# shell: 可以指定 shell 類型進行執行,例如 bash、pwsh、python、sh、cmd、powershell
# shell: bash
#
# env: 除了可以設置 workflow 以及 job 的 env,也可以設置 step 的 env(可以理解為作用域不同,局部作用域的優先級更高)
#
# comtinue-on-error: 默認當前 step 失敗則會阻止當前 job 繼續執行,設置 true 時當前 step 失敗則可以跳過當前 job 的執行
- name: Cache
# cache action: https://github.com/actions/cache
# cache 在這裡主要用於緩存 npm,提升構建速率
uses: actions/cache@v2
# npm 緩存的路徑可查看 https://docs.npmjs.com/cli/cache#cache
# 由於這裡 runs-on 是 ubuntu-latest,因此配置 ~/.npm
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
# github-script action: https://github.com/actions/github-script
# 在 workflow 中使用 Script 語法調用 Github API 或引用 workflow context
# setup-node action: https://github.com/actions/setup-node
# 配置 Node 執行環境(當前構建的伺服器默認沒有 Node 環境,可以通過 Action 安裝 Node)
# 需要注意安裝 Node 的同時會捆綁安裝 npm,如果想了解為什麼會捆綁,可以 Google 一下有趣的故事哦
# 因此使用了該 action 後就可以使用 npm 的腳本在伺服器進行執行啦
# 這裡也可以嘗試 v2-beta 版本哦
- name: Set Node
uses: actions/setup-node@v1
with:
# 也可以通過 strategy.matrix.node 進行靈活配置
# 這裡本地使用 node 的 12 版本構建,因此這裡就進行版本固定啦
node-version: "12"
- run: npm install
- run: npm run build
- run: npm run docs:build
- name: Deploy
# 用於發布靜態站點資源
# actions-gh-pages action: https://github.com/peaceiris/actions-gh-pages
uses: peaceiris/actions-gh-pages@v3
with:
# GTIHUB_TOKEN:https://docs.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token
# Github 會在 workflow 中自動生成 GIHUBT_TOKEN,用於認證 workflow 的運行
github_token: ${{ secrets.GITHUB_TOKEN }}
# 靜態資源目錄設置
publish_dir: ./docs/.vuepress/dist
# 默認發布到 gh-pages 分支上,可以指定特定的發布分支
publish_branch: gh-pages1 # default: gh-pages
full_commit_message: ${{ github.event.head_commit.message }}
#
# timeout-minutes: 一個 job 執行的最大時間,默認是 6h,如果超過時間則取消執行
#
# strategy.matrix: 例如指定當前 job 的 node 版本列表、作業系統類型列表等
# strategy.fail-fast
# strategy.max-parallel
# continue-on-error: 一旦當前 job 執行失敗,那麼 workflow 停止執行。設置為 true 可以跳過當前 job 執行
# container: Docker 容器配置,包括 image、env、ports、volumes、options 等配置
#
# services: 使用 Docker 容器 Action 或者 服務 Action 必須使用 Linux 環境運行溫馨提示:這裡不再敘述具體的配置過程,更多可查看配置文件中貼出的連結信息。
上傳 CI 的配置文件後,Github 就會進行自動構建,具體如下:
正在構建或者構建完成後可查看每個構建的信息,如果初次構建失敗則可以通過構建信息找出失敗原因,並重新修改構建配置嘗試再次構建。除此之外,每次構建失敗 Github 都會通過郵件的形式進行通知:
如果構建成功,則每次你推送新的代碼後,Github 服務會進行一系列流程並自動更新靜態資源站點。
總結希望大家看完這篇文檔之後如果想使用其中某些工具能夠養成以下一些習慣:
通篇閱讀工具的文檔,了解相同功能的不同工具的差異點通篇閱讀工具對應的官方 Github README 文檔以及官方站點文檔,了解該工具設計的核心哲學、核心功能、解決什麼核心問題。前端的工具百花齊放,同樣的功能可能可以採用多種不同的工具實現。如果想要在項目中使用適當的工具,就得知道這些工具的差異。完整的閱讀相應的官方文檔,有助於你理解各自的核心功能和差異。
在調研了各個工具的差異之後,選擇認為合適的工具進行實踐在實踐的過程中你會對該工具的使用越來越熟悉。此時如果遇到一些問題或者想要實現某些功能,在通篇閱讀文檔的基礎上會變得相對容易。當然如果遇到一些報錯信息無法解決,此時第一時間應該是搜索當前工具所對應的 Github Issues。除此之外,你也可以根據錯誤的堆棧信息追蹤工具的源碼,了解源碼之後可能會對錯誤信息產生的原因更加清晰。
在完成以上兩步之後,你應該總結工具的使用技巧啦,此時在此通讀工具文檔可能會產生不一樣的收穫 連結文檔 Top 10 JavaScript errors from 1000+ projects (and how to avoid them)[166]前端構建:3 類 13 種熱門工具的選型參考[167]ES modules: A cartoon deep-dive[169](強烈推薦閱讀)New to front-end testing? Start from the top of the pyramid![171]JavaScript & Node.js Testing Best Practices[172][譯] JavaScript 單元測試框架:Jasmine, Mocha, AVA, Tape 和 Jest 的比較[173]JavaScript unit testing frameworks in 2020: A comparison[174]javascript-testing-best-practices[175]ui-testing-best-practices[176]Introduction to CI/CD with GitLab(中文版)[179]當我有伺服器時我做了什麼 · 個人伺服器運維指南[181]參考資料[1]子弈: https://juejin.im/user/3227821870163176/posts
[2]從零開始配置 TypeScript 項目: https://juejin.im/post/6856410900577026061
[3]面試分享:兩年工作經驗成功面試阿里 P6 總結: https://juejin.im/post/6844903928442667015
[4]在阿里我是如何當面試官的: https://juejin.im/post/6844904093425598471
[5]算法與 TypeScript 實現: https://github.com/ziyi2/algorithms
[6]算法與 TypeScript 實現: https://github.com/ziyi2/algorithms
[7]feat/framework: https://github.com/ziyi2/algorithms/tree/feat/framework
[8]學習文檔: https://ziyi2.github.io/algorithms/
[9]tsdx: https://github.com/formik/tsdx#readme
[10]microbundle: https://github.com/developit/microbundle
[11]typescript-starter: https://github.com/bitjson/typescript-starter
[12]ts-lib-scripts: https://github.com/sinoui/ts-lib-scripts
[13]afaa458: https://github.com/ziyi2/algorithms/commit/afaa4583009ea5ac3ead2f3bfc5c61103ce8533c
[14]d04e259: https://github.com/ziyi2/algorithms/commit/d04e25977a7041b5e2d9d801934d554ab6815c42
[15]ebecee9: https://github.com/ziyi2/algorithms/commit/ebecee96551f8ed49a7b48c61be3da6b79ae3974
[16]dca67d4: https://github.com/ziyi2/algorithms/commit/dca67d4da73259636c612e677d7d406903d7abd8
[17]7f3487a: https://github.com/ziyi2/algorithms/commit/7f3487a65f3325a9964d1ee462941f138f299f42
[18]b440186: https://github.com/ziyi2/algorithms/commit/b440186dbd8ac4052fe3715882c8fe86c495a4ae
[19]6f086f2: https://github.com/ziyi2/algorithms/commit/6f086f27ac16be565f2cd4f49a310ad277571e08
[20]93e597a: https://github.com/ziyi2/algorithms/commit/93e597a1cf9bc3d9ea6ba4c1e5ba18c4cb4575fe
[21]66e38d1: https://github.com/ziyi2/algorithms/commit/66e38d1ec9965846d5e1928e58dcfcd9967307d7
[22]1cc85a4: https://github.com/ziyi2/algorithms/commit/1cc85a4ae4d6c378e8896a35de60565e2f72f865
[23]CHANGELOG.md: https://github.com/ziyi2/algorithms/blob/feat/framework/CHANGELOG.md
[24]Commitizen: https://github.com/commitizen/cz-cli
[25]Cz 工具集使用介紹: https://juejin.im/post/5cc4694a6fb9a03238106eb9
[26]cz-customizable: https://github.com/leonardoanalista/cz-customizable
[27]commitlint: https://commitlint.js.org/#/
[28]conventional-changelog: https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog
[29]husky: https://github.com/typicode/husky
[30]使用 NPM 發布和使用 CLI 工具: https://juejin.im/post/5eb89053e51d454de54db501
[31]Top 10 JavaScript errors from 1000+ projects (and how to avoid them): https://rollbar.com/blog/top-10-javascript-errors/
[32]Integrating with Build Tools: https://www.typescriptlang.org/docs/handbook/integrating-with-build-tools.html
[33]Tree Shaking: https://github.com/rollup/rollup
[34]ES Module: https://github.com/rollup/rollup/wiki/ES6-modules
[35]@babel/preset-typescript: https://babeljs.io/docs/en/babel-preset-typescript
[36]@babel/plugin-transform-typescript - Caveats: https://www.babeljs.cn/docs/babel-plugin-transform-typescript#caveats
[37]Babel 7 or TypeScript: https://kulshekhar.github.io/ts-jest/user/babel7-or-ts
[38]TypeScript - Building: https://github.com/microsoft/TypeScript#building
[39]TypeScript 中文網 - Gulp: https://www.tslang.cn/docs/handbook/gulp.html
[40]esbuild: https://github.com/evanw/esbuild
[41]parcel: https://github.com/parcel-bundler/parcel
[42]backpack: https://github.com/jaredpalmer/backpack
[43]前端構建:3 類 13 種熱門工具的選型參考: https://segmentfault.com/a/1190000017183743
[44]gulp-typescript: https://github.com/ivogabe/gulp-typescript
[45]tsconfig.json: https://www.tslang.cn/docs/handbook/tsconfig-json.html
[46]TypeScript 官方文檔 / 編譯選項: https://www.tslang.cn/docs/handbook/compiler-options.html
[47]ES modules: A cartoon deep-dive: https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/
[48]ES6 modules: https://github.com/rollup/rollup/wiki/ES6-modules
[49]pkg.module: https://github.com/rollup/rollup/wiki/pkg.module
[50]rimfaf: https://github.com/isaacs/rimraf
[51]files: https://docs.npmjs.com/files/package.json#files
[52]main: https://docs.npmjs.com/files/package.json#main
[53].eslintrc.json: https://github.com/microsoft/TypeScript/blob/master/.eslintrc.jso
[54]自定義解析器: https://cn.eslint.org/docs/developer-guide/working-with-custom-parsers
[55]ESTree: https://github.com/estree/estree
[56]@typescript-eslint/parser: https://github.com/typescript-eslint/typescript-eslint#getting-started--installation
[57]@typescript-eslint/typescript-estree: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/typescript-estree
[58]Lerna: https://github.com/lerna/lerna
[59]@typescript-eslint/eslint-plugin: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin
[60]@typescript-eslint/eslint-plugin-tslint: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin-tslint
[61]Migrating from TSLint to ESLint: https://github.com/typescript-eslint/typescript-eslint#migrating-from-tslint-to-eslint
[62]... ]`:在 ESLint 中使用[共享規則配置: https://cn.eslint.org/docs/developer-guide/shareable-configs
[63]TypeScript Supported Rules: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#supported-rules
[64]ESLint Formatters: https://cn.eslint.org/docs/user-guide/formatters/
[65]stylish formatter: https://cn.eslint.org/docs/user-guide/formatters/#stylish
[66]ESLint 插件: https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint
[67]ESLint 退出代碼: https://cn.eslint.org/docs/user-guide/command-line-interface#exit-codes
[68]Why Prettier?: https://prettier.io/docs/en/why-prettier.html
[69]Prettier vs Linters: https://prettier.io/docs/en/comparison.html
[70]max-len: https://eslint.org/docs/rules/max-len
[71]keyword-spacing: https://eslint.org/docs/rules/keyword-spacing
[72]no-mixed-spaces-and-tabs: https://eslint.org/docs/rules/no-mixed-spaces-and-tabs
[73]no-unused-vars: https://eslint.org/docs/rules/no-unused-vars
[74]no-implicit-globals: https://eslint.org/docs/rules/no-implicit-globals
[75]prefer-promise-reject-errors: https://eslint.org/docs/rules/prefer-promise-reject-errors
[76]Integrating with Linters: https://prettier.io/docs/en/integrating-with-linters.html
[77]Option Philosophy: https://prettier.io/docs/en/option-philosophy.html
[78]eslint-config-prettier: https://github.com/prettier/eslint-config-prettier
[79]命令行接口: https://prettier.io/docs/en/cli.html
[80]CLI helper tool: https://github.com/prettier/eslint-config-prettier#cli-helper-tool
[81]Prettier - Code formatter: https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode
[82]Prettier - Pre-commit Hook: https://prettier.io/docs/en/precommit.html
[83]commitlint: https://commitlint.js.org/#/
[84]Git Hook 官方文檔: https://git-scm.com/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-Git-%E9%92%A9%E5%AD%90
[85]Git 鉤子:自定義你的工作流: https://github.com/geeeeeeeeek/git-recipes/wiki/5.4-Git-%E9%92%A9%E5%AD%90%EF%BC%9A%E8%87%AA%E5%AE%9A%E4%B9%89%E4%BD%A0%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81
[86]ghooks: https://github.com/ghooks-org/ghooks
[87]鉤子屬性配置: https://github.com/ghooks-org/ghooks#setup
[88]husky: https://github.com/typicode/husky
[89]pre-commit: https://github.com/pre-commit/pre-commit
[90]使用 NPM 發布和使用 CLI 工具: https://juejin.im/post/5eb89053e51d454de54db501
[91]ghooks - hook.template.raw: https://github.com/ghooks-org/ghooks/blob/master/lib/hook.template.raw
[92]commitlint: https://commitlint.js.org/#/
[93]lint-staged: https://github.com/okonet/lint-staged
[94]Git 鉤子: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
[95]JavaScript 程序測試: https://javascript.ruanyifeng.com/tool/testing.html
[96]New to front-end testing? Start from the top of the pyramid!: https://dev.to/noriste/new-to-front-end-testing-start-from-the-top-of-the-pyramid-36kj
[97][譯] JavaScript 單元測試框架:Jasmine, Mocha, AVA, Tape 和 Jest 的比較: https://juejin.im/post/5acc721a6fb9a028b77b23c9
[98]JavaScript unit testing frameworks in 2020: A comparison: https://raygun.com/blog/javascript-unit-testing-frameworks/
[99]javascript-testing-best-practices: https://github.com/goldbergyoni/javascript-testing-best-practices/blob/master/readme-zh-CN.md
[100]ui-testing-best-practices: https://github.com/NoriSte/ui-testing-best-practices
[101]Jest: https://jestjs.io/
[102]testcafe: https://github.com/DevExpress/testcafe
[103]cypress: https://github.com/cypress-io/cypress
[104]Jest: https://jestjs.io/en/
[105]Jest - Using TypeScript: https://jestjs.io/docs/en/getting-started#using-typescript
[106]Babel 7 or TypeScript: https://kulshekhar.github.io/ts-jest/user/babel7-or-ts
[107]ts-jest: https://kulshekhar.github.io/ts-jest/user/install#customizing
[108]Jest CLI Options: https://jestjs.io/docs/zh-Hans/cli
[109]coveralls: https://coveralls.io/
[110]lint-staged - Use environment variables with linting commands: https://github.com/okonet/lint-staged#use-environment-variables-with-linting-commands
[111]testMatch: https://jestjs.io/docs/zh-Hans/configuration#testmatch-arraystring
[112]testRegex: https://jestjs.io/docs/zh-Hans/configuration#testregex-string--arraystring
[113]awesome-jest: https://github.com/jest-community/awesome-jest
[114]eslint-plugin-jest: https://github.com/jest-community/eslint-plugin-jest
[115]ts-jest/.eslintrc.js: https://github.com/kulshekhar/ts-jest/blob/master/.eslintrc.js#L12
[116]no-identical-title: https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/no-identical-title.md
[117]eslint-plugin-jest-formatting: https://github.com/dangreenisrael/eslint-plugin-jest-formatting
[118]鉤子: https://www.npmjs.cn/misc/scripts/#description
[119]husky - package.json: https://github.com/typicode/husky/blob/master/package.json
[120]npm-scripts: https://www.npmjs.cn/misc/scripts/
[121]npm scripts 使用指南: http://www.ruanyifeng.com/blog/2016/10/npm_scripts.html?utm_source=tuicool&utm_medium=referral
[122]tsdoc: https://github.com/Microsoft/tsdoc
[123]jsdoc: https://github.com/jsdoc/jsdoc
[124]esdoc: https://github.com/esdoc/esdoc
[125]Document This For jsdoc: https://marketplace.visualstudio.com/items?itemName=joelday.docthis
[126]TSDoc Comment: https://marketplace.visualstudio.com/items?itemName=kingsimba.tsdoc-comment
[127]vue-markdown-loader: https://github.com/QingWei-Li/vue-markdown-loader#with-vue-cli-3
[128]Vuepress: https://www.vuepress.cn/
[129]dumi: https://d.umijs.org/guide
[130]react-markdown: https://github.com/rexxars/react-markdown#readme
[131]react-static: https://github.com/react-static/react-static
[132]Latex: https://www.latex-project.org/
[133]快速上手: https://www.vuepress.cn/guide/getting-started.html#%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B
[134]目錄結構: https://www.vuepress.cn/guide/directory-structure.html
[135]默認主題 / 首頁: https://www.vuepress.cn/theme/default-theme-config.html#%E9%A6%96%E9%A1%B5
[136]配置: https://www.vuepress.cn/config/#%E9%85%8D%E7%BD%AE
[137]導航欄: https://www.vuepress.cn/theme/default-theme-config.html#%E5%AF%BC%E8%88%AA%E6%A0%8F
[138]側邊欄: https://www.vuepress.cn/theme/default-theme-config.html#%E4%BE%A7%E8%BE%B9%E6%A0%8F
[139]插件: https://www.vuepress.cn/plugin/
[140]使用組件: https://www.vuepress.cn/guide/using-vue.html#%E4%BD%BF%E7%94%A8%E7%BB%84%E4%BB%B6
[141]Webpack 構建: https://www.vuepress.cn/config/#chainwebpack
[142]webpack-chain: https://github.com/neutrinojs/webpack-chain
[143]Vuepress - 構建流程 - configurewebpack: https://www.vuepress.cn/config/#configurewebpack
[144]awesome-vuepress: https://github.com/vuepressjs/awesome-vuepress
[145]Vuepress 社區: https://vuepress.github.io/zh/
[146]markdownlint: https://github.com/DavidAnson/markdownlint
[147]markdownlint: https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint
[148]markdownlint-cli: https://github.com/igorshubovych/markdownlint-cli
[149]技術文章的寫作技巧分享: https://juejin.im/post/5ecbdff6e51d45783e17a7a1
[150]Introduction to CI/CD with GitLab(中文版): https://s0docs0gitlab0com.icopy.site/ee/ci/introduction/index.html
[151]GitHub Actions 入門教程: http://www.ruanyifeng.com/blog/2019/09/getting-started-with-github-actions.html
[152]Github Actions 官方文檔: https://docs.github.com/en/actions
[153]當我有伺服器時我做了什麼 · 個人伺服器運維指南: https://shanyue.tech/op/#%E9%A2%84%E8%A7%88
[154]Introduction to CI/CD with GitLab(中文版): https://s0docs0gitlab0com.icopy.site/ee/ci/introduction/index.html
[155]webhook: https://docs.github.com/en/actions/reference/events-that-trigger-workflows
[156]限制: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#usage-limits
[157]官方 Actions: https://github.com/marketplace?type=actions
[158]awesome-actions: https://github.com/sdras/awesome-actions
[159]官方 Actions: https://github.com/marketplace?type=actions
[160]awesome-actions: https://github.com/sdras/awesome-actions
[161]Checkout: https://github.com/actions/checkout
[162]cache: https://github.com/actions/cache
[163]setup-node: https://github.com/actions/setup-node
[164]actions-gh-pages: https://github.com/peaceiris/actions-gh-pages
[165]使用 NPM 發布和使用 CLI 工具: https://juejin.im/post/5eb89053e51d454de54db501
[166]Top 10 JavaScript errors from 1000+ projects (and how to avoid them): https://rollbar.com/blog/top-10-javascript-errors/
[167]前端構建:3 類 13 種熱門工具的選型參考: https://segmentfault.com/a/1190000017183743
[168]Cz 工具集使用介紹: https://juejin.im/post/5cc4694a6fb9a03238106eb9
[169]ES modules: A cartoon deep-dive: https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/
[170]JavaScript 程序測試: https://javascript.ruanyifeng.com/tool/testing.html
[171]New to front-end testing? Start from the top of the pyramid!: https://dev.to/noriste/new-to-front-end-testing-start-from-the-top-of-the-pyramid-36kj
[172]JavaScript & Node.js Testing Best Practices: https://github.com/goldbergyoni/javascript-testing-best-practices/blob/master/readme-zh-CN.md
[173][譯] JavaScript 單元測試框架:Jasmine, Mocha, AVA, Tape 和 Jest 的比較: https://juejin.im/post/5acc721a6fb9a028b77b23c9
[174]JavaScript unit testing frameworks in 2020: A comparison: https://raygun.com/blog/javascript-unit-testing-frameworks/
[175]javascript-testing-best-practices: https://github.com/goldbergyoni/javascript-testing-best-practices/blob/master/readme-zh-CN.md
[176]ui-testing-best-practices: https://github.com/NoriSte/ui-testing-best-practices
[177]npm scripts 使用指南: http://www.ruanyifeng.com/blog/2016/10/npm_scripts.html?utm_source=tuicool&utm_medium=referral
[178]技術文章的寫作技巧分享: https://juejin.im/post/5ecbdff6e51d45783e17a7a1
[179]Introduction to CI/CD with GitLab(中文版): https://s0docs0gitlab0com.icopy.site/ee/ci/introduction/index.html
[180]GitHub Actions 入門教程: http://www.ruanyifeng.com/blog/2019/09/getting-started-with-github-actions.html
[181]當我有伺服器時我做了什麼 · 個人伺服器運維指南: https://shanyue.tech/op/#%E9%A2%84%E8%A7%88
- END -