代码统一风格、代码规范和提交规范

399 阅读2分钟

1、安装模块

全局安装 eslint、commitlint 、 check-prettier

npm install eslint commitlint check-prettier -g

本地安装

npm install eslint-config-prettier  stylelint  stylelint-config-prettier stylelint-config-standard husky  @commitlint/config-conventional -D

VSCode 安装 Eslint和Prettier插件

setting.json配置
"eslint.validate": [
    "javascript",
    "javascriptreact"
],
"[html]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
},
"diffEditor.ignoreTrimWhitespace": false,
"[typescriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.accessibilitySupport": "off",
"editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
},

2、package 脚本

"scripts": {
    "start": "cross-env PORT=3100 REACT_APP_ENV=dev TEAMIND_ENV=dev node scripts/start.js",
    "start:https": "cross-env PORT=3100 REACT_APP_ENV=dev HTTPS=https TEAMIND_ENV=dev node scripts/start.js",
    "build": "GENERATE_SOURCEMAP=false node scripts/build.js && node scripts/upload.js",
    "lint": "npm run lint:fix && npm run lint:prettier && eslint --ext .js,.jsx src",
    "lint:fix": "eslint --fix --ext .js,.jsx src && npx stylelint --fix 'src/**/*.less' --syntax less",
    "lint:prettier": "check-prettier lint",
    "prettier": "node ./scripts/prettier.js",
    "prepare": "husky install && npx husky add .husky/pre-commit 'npm run lint \n npm run prettier' && npx husky add .husky/commit-msg 'npx --no-install commitlint --edit `$1`' "
},

3、增加自动化脚本

scripts/getPrettierFiles.js

const glob = require('glob')

const getPrettierFiles = () => {
  let files = []
  const jsFiles = glob.sync('src/**/*.js*', { ignore: ['**/node_modules/**', 'build/**'] })
  const tsFiles = glob.sync('src/**/*.ts*', { ignore: ['**/node_modules/**', 'build/**'] })
  const configFiles = glob.sync('config/**/*.js*', { ignore: ['**/node_modules/**', 'build/**'] })
  const scriptFiles = glob.sync('scripts/**/*.js')
  const lessFiles = glob.sync('src/**/*.less*', { ignore: ['**/node_modules/**', 'build/**'] })
  files = files.concat(jsFiles)
  files = files.concat(tsFiles)
  files = files.concat(configFiles)
  files = files.concat(scriptFiles)
  files = files.concat(lessFiles)
  if (!files.length) {
    return
  }
  return files
}

module.exports = getPrettierFiles

scripts/lint-prettier.js

/**
 * copy to https://github.com/facebook/react/blob/master/scripts/prettier/index.js
 * prettier api doc https://prettier.io/docs/en/api.html
 *----------*****--------------
 *  lint file is prettier
 *----------*****--------------
 */

 const prettier = require('prettier')
 const fs = require('fs')
 const chalk = require('chalk')
 
 const prettierConfigPath = require.resolve('../.prettierrc')
 
 const files = process.argv.slice(2)
 
 let didError = false
 
 files.forEach(file => {
   Promise.all([
     prettier.resolveConfig(file, {
       config: prettierConfigPath,
     }),
     prettier.getFileInfo(file),
   ])
     .then(resolves => {
       const [options, fileInfo] = resolves
       if (fileInfo.ignored) {
         return
       }
       const input = fs.readFileSync(file, 'utf8')
       const withParserOptions = {
         ...options,
         parser: fileInfo.inferredParser,
       }
       const output = prettier.format(input, withParserOptions)
       if (output !== input) {
         fs.writeFileSync(file, output, 'utf8')
         // console.log(chalk.green(`${file} is prettier`));
       }
     })
     .catch(e => {
       didError = true
     })
     .finally(() => {
       if (didError) {
         process.exit(1)
       }
       console.log(chalk.hex('#1890FF')('prettier success!'))
     })
 })

scripts/prettier.js

/**
 * copy to https://github.com/facebook/react/blob/master/scripts/prettier/index.js
 * prettier api doc https://prettier.io/docs/en/api.html
 *----------*****--------------
 *  prettier all js and all ts.
 *----------*****--------------
 */

 const prettier = require('prettier')
 const fs = require('fs')
 const getPrettierFiles = require('./getPrettierFiles')
 
 const prettierConfigPath = require.resolve('../.prettierrc')
 const chalk = require('chalk')
 
 let didError = false
 
 const files = getPrettierFiles()
 
 files.forEach(file => {
   const options = prettier.resolveConfig.sync(file, {
     config: prettierConfigPath,
   })
   const fileInfo = prettier.getFileInfo.sync(file)
   if (fileInfo.ignored) {
     return
   }
   try {
     const input = fs.readFileSync(file, 'utf8')
     const withParserOptions = {
       ...options,
       parser: fileInfo.inferredParser,
     }
     const output = prettier.format(input, withParserOptions)
     if (output !== input) {
       fs.writeFileSync(file, output, 'utf8')
       console.log(chalk.green(`${file} is prettier`))
     }
   } catch (e) {
     didError = true
   }
 })
 
 if (didError) {
   process.exit(1)
 }
 console.log(chalk.hex('#1890FF')('prettier success!'))

4、配置文件

.prettierrc

{
  "printWidth": 120,
  "semi": false,
  "trailingComma": "es5",
  "bracketSpacing": true,
  "jsxBracketSameLine": false, 
  "singleQuote": true,
  "jsxSingleQuote":false,
  "overrides": [
    {
      "files": ".prettierrc",
      "options": { "parser": "json" }
    }
  ]
}

commitlint.config.js

module.exports = {
    extends: [
      '@commitlint/config-conventional'
    ],
    rules: {
      'type-enum': [2, 'always', [
        'feat', 'fix', 'refactor', 'docs', 'chore', 'style', 'revert'
       ]],
      'type-case': [0],
      'type-empty': [0],
      'scope-empty': [0],
      'scope-case': [0],
      'subject-full-stop': [0, 'never'],
      'subject-case': [0, 'never'],
      'header-max-length': [0, 'always', 72]
    }
  }

.eslintrc

{
  "parser": "babel-eslint",
  "extends": ["react-app", "prettier"],
  "plugins": [
    // ...
    "react-hooks"
  ],
  "env": {
    "browser": true,
    "node": true,
    "es6": true,
    "mocha": true,
    "jest": true,
    "jasmine": true
  },
  "rules": {
    // ...
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "error",
    "quotes": ["error", "single", { "allowTemplateLiterals": true }],
    "semi": ["error", "never"],
    "eqeqeq": "off",
    "no-use-before-define": "off",
    "no-unused-vars": "off",
    "array-callback-return": "off",
    "no-throw-literal": "off"
  }
}

.stylelintrc.json

{
    "extends": ["stylelint-config-standard", "stylelint-config-prettier"],
    "rules": {
      "no-descending-specificity": null, 
      "no-empty-source": null,
      "no-duplicate-selectors": null,
      "font-family-no-missing-generic-family-keyword": null,
      "selector-pseudo-class-no-unknown": null
    }
 }

5、安装husky

npm i husky -D 初始化 npm run  prepare

# Activate hooks
npx husky install
# or
yarn husky install

# Add hook
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
npx husky add .husky/pre-commit 'npm run prettier npm run lint'

6、 使用

配置后 先执行 npm run prepare 初始化 husky 然后按照规范提交代码即可自动执行全局格式化

7、 问题与解决

通过使用发现的问题与解决方案 问题

  • prettier为全局美化代码, 多个文件的提交时, 全局格式化后,会有部分文件美化后,没有提交上去, 需要二次提交, 或者git commit --ament --no-edit
  • Eslint 检查也为全局检查, 会影响提交速度 解决方案: 安装 "lint-staged" 配置package.json
 "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint",
      "npm run prettier",
      "git add"
    ],
    "*.{less,scss}": "npx stylelint --fix src/**/*.less --syntax less"
  }

修改.husky

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

# npm run lint 
# npm run prettier
npx lint-staged

8、参考链接

husky: Husky - Git hooks
使用husky规范commit记录: cloud.tencent.com/developer/a…