Pre Commit

最終效果:每次提交代碼時(shí),自動(dòng)完成以下兩個(gè)功能

  1. 執(zhí)行代碼增量檢查,有代碼風(fēng)格錯(cuò)誤拋出異常。

  2. 檢查提交的文件列表,如果同時(shí)存在 dist 目錄和非 dist 目錄,拋出異常。

前期準(zhǔn)備

在閱讀本文之前,請(qǐng)確保熟悉或了解 eslinteslint-stagedhusky.

如果不了解,請(qǐng)點(diǎn)擊傳送門:Javascript代碼檢查那點(diǎn)事

代碼增量檢查

eslint 的具體配置不再贅述,在上述的傳送門中有詳細(xì)配置。

安裝相關(guān)依賴

npm install --save-dev husky
npm install --save-dev lint-staged

package.json 中增加如下配置

"husky": {
  "hooks": {
    "pre-commit": "node scripts/utils/pre-commit.js"
  }
},
"lint-staged": {
  "linters": {
    "*.js": [
      "eslint",
      "git add"
    ],
    "*.vue": [
      "eslint",
      "git add"
    ]
  },
  "ignore": ["dist/**"]
},

配置說明:當(dāng)執(zhí)行 git commit 時(shí),會(huì)執(zhí)行 husky.hooks.pre-commit, 也就是 node scripts/utils/pre-commit.js,該命令為執(zhí)行一段腳本,腳本如下。

提交文件檢查

檢查提交內(nèi)容的核心思路為:獲取本次提交的改動(dòng)文件列表信息 -> 檢查文件路徑 -> 判斷是否同時(shí)存在 dist 和非 dist目錄,如果同時(shí)存在,則拋出異常停止 commit 操作,如果不存在,則執(zhí)行 eslint 代碼檢查。詳細(xì)代碼如下(可直接拷貝到 scripts/utils/pre-commit.js 中)

const chalk = require('chalk')
const symbols = require('log-symbols')
const spawn = require('child_process').spawn
require('lint-staged')

// 檢查改動(dòng)的文件目錄
getDiffFiles().then(files => {
  const filePaths = files.map(file => file.filename.split('/')[0])
  let isDistFolder = false
  let isOtherFiles = false
  filePaths.forEach(path => {
    if (path === 'dist') isDistFolder = true
    else isOtherFiles = true
  })
  if (isDistFolder && isOtherFiles) {
    throw new Error()
  }
  runCmd('lint-staged')
}).catch(() => {
  /* eslint-disable */
  console.error(`\n\n${symbols.error} ${chalk.redBright('Ops!Dist folder and other files cannot be submitted at the same time.')}`)  
  console.log(`    (use "git reset" to cancel the "add" operation)\n`)
  /* eslint-enable */
  process.exit(1)
})


// 獲取本次改動(dòng)的文件列表
function getDiffFiles() {
  return getHeadCommitId().then(head => {
    if (head) {
      const command = 'git -c core.quotepath=false diff-index --cached --name-status -M --diff-filter=ACM ' + head
      return runCmd(command).then(({ err, stdout, stderr }) => {
        return err || stderr ? err || new Error(stderr) : stdoutToResultsObject(stdout)
      })
    }
  })
}

// 獲取最近一次提交的commit_id
function getHeadCommitId() {
  return runCmd('git rev-parse --verify HEAD').then(({ err, stdout, stderr }) => {
    if (err && err.message.indexOf('fatal: Needed a single revision') !== -1) {
      return getFirstCommitId()
    } else {
      return err || stderr ? err || new Error('STDERR: ' + stderr) : stdout.replace('\n', '')
    }
  })
}

// 獲取第一次提交的commit_id
function getFirstCommitId() {
  return runCmd('git hash-object -t tree /dev/null').then(({ err, stdout, stderr }) => {
    return err || stderr ? err || new Error('STDERR: ' + stderr) : stdout.replace('\n', '')
  })
}


// 執(zhí)行命令,監(jiān)聽控制臺(tái)信息
function runCmd(command) {
  return new Promise((resolve) => {
    // 解析命令獲取參數(shù)
    const bits = command.split(' ')
    const args = bits.slice(1)

    // 執(zhí)行命令
    const cmd = spawn(bits[0], args, { cwd: process.cwd() })

    let stdout = ''
    let stderr = ''

    cmd.stdout.on('data', data => {
      stdout += data.toString()
    })

    cmd.stderr.on('data', data => {
      stderr += data.toString()
    })

    cmd.on('close', code => {
      const err = code !== 0 ? new Error(stderr) : null
      resolve({ err, stdout, stderr })
    })
  })
}

// 把stdout輸出信息轉(zhuǎn)化成Object對(duì)象
function stdoutToResultsObject(stdout) {
  const results = []
  const lines = stdout.split('\n')
  let iLines = lines.length
  while (iLines--) {
    const line = lines[iLines]
    if (line !== '') {
      const parts = line.split('\t')
      const result = {
        filename: parts[2] || parts[1],
        status: codeToStatus(parts[0]),
      }
      results.push(result)
    }
  }
  return results
}

// git 枚舉映射
function codeToStatus(code) {
  const map = {
    A: 'Added',
    C: 'Copied',
    D: 'Deleted',
    M: 'Modified',
    R: 'Renamed',
    T: 'Type-Change',
    U: 'Unmerged',
    X: 'Unknown',
    B: 'Broken',
  }
  return map[code.charAt(0)]
}

當(dāng)執(zhí)行 git commit 操作時(shí),首先會(huì)執(zhí)行上述腳本,當(dāng)滿足要求時(shí),再執(zhí)行 eslint 代碼檢查,當(dāng)代碼檢查通過后,才會(huì)提交代碼到遠(yuǎn)程倉庫!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、前言 現(xiàn)在最流行的版本管理工具非git莫屬,而良好的代碼規(guī)范有助于項(xiàng)目的維護(hù),為了防止一些不規(guī)范的代碼 co...
    前端妹子ice閱讀 53,588評(píng)論 2 17
  • 良好的代碼規(guī)范有助于項(xiàng)目的維護(hù)和新人的快速上手。前段時(shí)間,把eslint引入了項(xiàng)目中做靜態(tài)代碼檢查。 一下把所有的...
    du_4c0c閱讀 13,704評(píng)論 0 7
  • git掛鉤簡介 git hooks是一些自定義的腳本,用于控制git工作的流程,分為客戶端鉤子和服務(wù)端鉤子。 客戶...
    ITgecko閱讀 50,099評(píng)論 2 7
  • 小程序, mpvue. 腳手架里有配置eslint. 為了保證前端代碼質(zhì)量, 決定再配置 pre-commit....
    小小小魔仙閱讀 934評(píng)論 0 0
  • 什么叫認(rèn)知 經(jīng)常聽到認(rèn)知這個(gè)詞,但是它到底是什么意思呢?它跟我們的知識(shí)高低無關(guān),也跟財(cái)富多寡關(guān)聯(lián)不大。簡言之,它是...
    彩玥閱讀 1,361評(píng)論 0 1

友情鏈接更多精彩內(nèi)容