Script
- 創建文件
touch uniformCodeStyle.sh
- Edit 文件
#!/usr/bin/env bash
yarn add eslint --dev
yarn run eslint --init
touch .eslintignore
echo .eslintrc.js > .eslintignore
yarn add eslint-plugin-bowen-lint eslint-plugin-jsx-a11y eslint-plugin-react-hooks@latest @typescript-eslint/parser -D
# first ] line number
line=$(grep -n ']' .eslintrc.js | sed -n "1"p | cut -d ":" -f 1)
commaLine=`expr $line - 1`
# MacOS sed command
sed -i '' "${commaLine} s/$/,/" .eslintrc.js
sed -i '' "${line}i\\
\ \"plugin:bowen-lint/reactLint\"
" .eslintrc.js
insert='\ quotes: ["error", "single", { allowTemplateLiterals: true }],\
\ indent: ["error", 2],\
\ "comma-dangle": ["error", "never"],\
\ "react/jsx-indent": ["error", 2],\
\ "no-console": ["error", { allow: ["warn", "error"] }],\
\ "react/jsx-tag-spacing": [\
\ "error",\
\ {\
\ closingSlash: "never",\
\ beforeSelfClosing: "allow",\
\ afterOpening: "never",\
\ beforeClosing: "allow"\
\ }\
\ ],\
\ "object-curly-spacing": ["warn", "never"],\
\ "react/jsx-indent-props": ["off", "tab"],\
\ "import/no-unresolved": "off",\
\ "no-unused-vars": "off",\
\ "@typescript-eslint/no-unused-vars": ["error"] \
'
sed -i '' "/rules/ a\\
$insert" .eslintrc.js
sed -i '' "/rules/ a\\ ${insert}" .eslintrc.js
yarn add --dev --exact prettier
touch .prettierrc.js
echo "module.exports = {
singleQuote: true,
printWidth: 100,
tabWidth: 2,
useTabs: false,
semi: true,
quoteProps: 'as-needed',
jsxSingleQuote: false,
trailingComma: 'none',
bracketSpacing: true,
jsxBracketSameLine: false,
arrowParens: 'always',
rangeStart: 0,
rangeEnd: Infinity,
requirePragma: false,
insertPragma: false,
proseWrap: 'preserve',
htmlWhitespaceSensitivity: 'css',
endOfLine: 'lf'
};" > .prettierrc.js
touch .prettierignore
echo "**/*.md
**/*.svg
**/*.ejs
**/*.html
package.json" > .prettierignore
yarn add lint-staged --dev
touch .lintstagedrc.js
echo "module.exports = {
'**/*.{js,jsx,tsx,ts,scss,md,json}': ['prettier --write', 'git add'],
'**/*.{js,jsx,ts,tsx}': 'yarn lint-staged:js',
}" > .lintstagedrc.js
yarn add @commitlint/{cli,config-conventional} -D
touch .commitlintrc.js
yarn add commitlint-config-bowen-lint -D
echo 'module.exports = {
"extends": [
"bowen-lint"
]
}' > .commitlintrc.js
yarn add husky@next --dev
yarn husky install
## MacOS sed command
sed -i '' "/scripts/ a\\
\ \"lint-staged:js\": \"eslint --ext .js,.jsx,.ts,.tsx\",
" package.json
npx husky add .husky/pre-commit "yarn lint-staged"
npx husky add .husky/commit-msg "yarn commitlint --edit"
- 執行
source uniformCodeStyle.sh
背景
隨著前端應用的大型化和復雜化,越來越多的前端工程師和團隊開始重視 JavaScript 代碼規范。ESLint 是一款插件化的 JavaScript 代碼靜態檢查工具, 其核心是通過對代碼解析得到的 AST(Abstract Syntax Tree,抽象語法樹)進行模式匹配,定位不符合約定規范的代碼。可以根據個人/團隊的代碼風格進行配置,如果想降低配置成本,也可以直接使用開源配置方案,例如 eslint-config-airbnb。之后再搭配一些輔助工具(例如 husky 和 lint-staged),可以使得整個流程會更加順暢。
Eslint
Find and fix problems in your JavaScript code.
-
Install
yarn add eslint --dev
-
Set up a configuration file
yarn run eslint --init
創建
.eslintrc.js
文件,并使用 eslint-plugin-react, @typescript-eslint/eslint-plugin, eslint-config-airbnb, eslint, eslint-plugin-import, eslint-plugin-jsx-a11y, eslint-plugin-react-hooks, @typescript-eslint/parser 等 package'off' or 0 - turn the rule off
'warn' or 1 - turn the rule on as a warning (doesn't affect exit code)
'error' or 2 - turn the rule on as an error (exit code will be 1)
-
View .eslintrc.js
module.exports = { "parser": {}, //定義ESLint的解析器 "extends": [], // 定義文件繼承的子規范 "plugins": [], // 定義了該eslint文件所依賴的插件 "env": {}, "rules": {} }
-
Individual Rules
rules: { indent: ['error', 4], quotes: ['error', 'single', { allowTemplateLiterals: true }], 'comma-dangle': ['error', 'never'], 'react/jsx-indent': ['error', 4], 'no-console': ['error', { allow: ['warn', 'error'] }], 'react/jsx-tag-spacing': [ 'error', { closingSlash: 'never', beforeSelfClosing: 'allow', afterOpening: 'never', beforeClosing: 'allow', }, ], 'object-curly-spacing': ['warn', 'never'], 'react/jsx-filename-extension': [ 'warn', { extensions: ['.js', '.jsx'] }, ], 'react/jsx-indent-props': ['off', 'tab'], 'no-use-before-define': 'off', }
-
Ignore
touch .eslintignore // .eslintignore .eslintrc.js
Prettier
Prettier is an opinionated code formatter.
-
Install
yarn add --dev --exact prettier
-
Config file
touch .prettierrc.js // .prettierrc.js module.exports = { ...fabric.prettier, "singleQuote": true, "trailingComma": "all", "printWidth": 80, };
-
Ignore file
touch .prettierignore // .prettierignore # Ignore artifacts: build **/*.md **/*.svg **/*.ejs **/*.html package.json
Eslint VS Prettier
這里說下 Eslint 與 Prettier 之間的區別,二者的側重點不同,前者是代碼規范檢查,如是否可以使用 var,尾逗號,函數括號前面是否留空格,便于團隊保 持比較統一的代碼風格,而 Prettier 則是代碼格式化插件,可以根據一定的規則對我們的 js、css、less、jsx、vue 等文件進行格式化,保證團隊輸出的代 碼是統一的,所以二者除了小部分規則有交集之外,二者是可以在我們的開發中相輔相成的。
Use Prettier for formatting and linters for catching bugs!
Husky
Husky improves your commits and more ?? woof!
You can use it to lint your commit messages, run tests, lint code, etc... when you commit or push. Husky supports
all Git hooks.
Husky 是一個 git 鉤子插件,支持所有的 Git Hooks 鉤子,我們可以在這些鉤子觸發的時候執行某些命令或者操作
-
Install
yarn add husky@next --dev
-
Enable Git hooks
yarn husky install
-
Edit
package.json
{ "private": true, "scripts": { "husky-test": "echo 'Hello world!'", "postinstall": "husky install" } }
-
Add a Hook
npx husky add .husky/pre-commit "yarn husky-test"
Husky 只是提供了提交時的鉤子,然而有時候我們處理的項目并不是新項目,這個時候,可能只想對本次提交的代碼,做代碼檢查,而不是對現有目錄內所有的文件做檢查,所以我們需要引入 lint-staged 這個插件
lint-staged
Run linters against staged git files and don't let ?? slip into your code base!
-
Install
yarn add lint-staged --dev
-
Configuration
touch .lintstagedrc.js
-
Edit script
// package.json script: { ... "lint-staged": "lint-staged", "lint": "npm run lint:js && npm run lint:prettier", "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src", "lint:prettier": "check-prettier lint", "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src", "prettier": "prettier -c --write \"**/*\"" }
-
Filtering files
module.exports = { '**/*.{js,jsx,tsx,ts,scss,md,json}': ['prettier --write', 'git add'], '**/*.{js,jsx,ts,tsx}': 'yarn lint-staged:js', }
-
Edit
.husky/_/pre-commit
yarn lint-staged
Commitlint
Helps your team adhering to a commit convention.
-
Install
yarn add @commitlint/{cli,config-conventional} -D
-
Configuration
touch .commitlintrc.js // .commitlintrc.js module.exports = { extends: ['@commitlint/config-conventional'], rules: { 'type-enum': [ 2, 'always', [ 'feat', // 新功能(feature) 'fix', // 修補bug 'docs', // 文檔(documentation) 'style', // 格式(不影響代碼運行的變動) 'refactor', // 重構(即不是新增功能,也不是修改bug的代碼變動) 'test', // 增加測試 'chore', // 構建過程或輔助工具的變動 ], ], 'scope-empty': [1, 'always'], 'body-leading-blank': [2, 'always'], 'footer-leading-blank': [2, 'always'], }, };
-
Husky
npx husky add .husky/commit-msg "yarn commitlint --edit"
進階
如果想要讓整個公司規模化地應用統一的 JavaScript 代碼規范,問題就會變得較為復雜
問題分析
技術層面
- 技術選型分散 - 團隊內工程技術選型往往并不統一,如 React/Vue、JavaScript/TypeScript 等。
- 技術場景更加廣泛 - 對于大型團隊,其開發場景一般不會局限在傳統 Web 領域內,往往還會涉及 Node.js、React Native、小程序、桌面應用(例如 Electron)等更廣泛的技術場景。
- 工程數量的增加和工程方案離散化導致 ESLint 方案的復雜度提升 - 這樣會進一步增加工程接入成本、升級成本和方案維護成本。
需要解決的問題
如何制定統一的代碼規范和對應的 ESLint 配置?
- 場景支撐 - 如何實現對場景差異的支持?如何保證不同場景間一致部分(例如 JavaScript 基礎語法)的規范一致性?
- 技術選型支撐 - 如何在支撐不同技術選型的前提下,保證基礎規則(例如縮進)的一致性?
- 可維護性 - 具體到規則配置上,能否提升可復用性?在方案升級迭代時成本是否可控?
解決方案
- 基礎層 - 制定統一的基礎語法和格式規范,提供通用的代碼風格和語法規則配置,例如縮進、尾逗號等等。
- 框架支撐層(可選) - 提供對通用的一些技術場景、框架的支持,包括 React、Vue 等;這一層借助開源社區的各種插件進行配置,并對各種框架的規則都進行了一定的調整。
- TypeScript 層(可選) - 這一層借助 typescript-eslint,提供對 TypeScript 的支持。
- 適配層(可選) - 提供對特殊場景的定制化支持,例如配合 prettier 使用、或者某些團隊的特殊規則訴求。
具體的實際項目中,可以靈活的選擇各層級、各類型的搭配,獲得和項目匹配的 ESLint 規則集。最終,形成了如下所示的 ESLint 配置集:
這種通過分層、分類的結構設計,還有利于后期的維護:
- 對基礎層的修改,只需修改一處即會全局生效。
- 對非基礎層某一部分的調整不會產生關聯性的影響。
- 如需擴展對某一類型的支持,只需關注這一類型的特殊規則配置。
Individual Plugin
Installation
You'll first need to install ESLint:
$ npm i eslint --save-dev
// yarn
$ yarn add eslint -D
Next, install eslint-plugin-bowen-lint
:
$ npm install eslint-plugin-bowen-lint --save-dev
// yarn
$ yarn add eslint-plugin-bowen-lint -D
Usage
Add bowen-lint
to the extends section of your .eslintrc
configuration file. You can omit the eslint-plugin-
prefix:
{
"extends": [
"plugin:bowen-lint/reactLint"
]
}
Necessary Config
- eslint-config-airbnb
Necessary Plugin
- eslint-plugin-import
Supported Config
- plugin:bowen-lint/reactLint
- plugin:bowen-lint/vueLint
- plugin:bowen-lint/tsLint
- plugin:bowen-lint/prettierLint
Why Plugin not Config?
- Plugins - which third-party plugins define additional rules, environments, configs, etc. for ESLint to use.
- Rules - which rules are enabled and at what error level.
- Config -> extends
-> Extending Configuration File
-> A configuration file, once extended, can inherit all the traits of another configuration file (including rules,
plugins, and language options) and modify all the options.
How to create eslint plugin
-
Install
npm i -g yo npm i -g generator-eslint
-
Create empty file
mkdir eslint-plugin-demo cd eslint-plugin-demo
-
Eslint:plugin
yo eslint:plugin
-
Eslint:rule
yo eslint:rule
注意:如果是
extends
的參數值,需要放置在package.js
的dependencies
屬性中
代碼集成檢查
在代碼 Commit 時,通過 GitHook 觸發 ESLint 檢查。其優點在于能實時響應開發者的動作,給出反饋,快速定位和修復問題;缺陷在于開發者可以主動跳過檢查。使用 Husky + Lint-staged 結合,在代碼 Commit 時,通過 GitHook 觸發對 git 暫存區文件的檢查。
Individual Config
Installation
$ npm install commitlint-config-bowen-lint --save-dev
// yarn
$ yarn add commitlint-config-bowen-lint -D
Usage
Add bowen-lint
to the extends section of your .commitlintrc.js
configuration file. You can omit
the commitlint-config-
prefix:
{
"extends": [
"bowen-lint"
]
}
Configuration
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat',
'fix',
'docs',
'style',
'refactor',
'test',
'chore',
],
],
'scope-empty': [1, 'always'],
'body-leading-blank': [2, 'always'],
'footer-leading-blank': [2, 'always'],
}