husky 原理

2,090 阅读1分钟

husky 原理

Install

yarn install husky lint-staged -D

Usage

  1. .git directory and package.json are at them same level
npm set-script prepare "husky install"
npm run prepare

Add a hook:

npx husky add .husky/pre-commit "yarn lint-staged"

Edit package.json

"scripts": {
  "lint-staged": "lint-staged",
  "prepare": "husky install",
  "lint": "eslint --ignore-path .gitignore \"**/*.{js,jsx}\"",
  "lint:fix": "yarn lint --fix"
},
"lint-staged": {
  "*.{js,jsx,ts,tsx}": [
    "yarn lint:fix"
  ],
  "*.css": [
    "prettier --write"
  ]
}
  1. .git directory and package.json are not at them same level. For example, project/.git and project/front/package.json.
npm set-script prepare "cd .. && husky install front/.husky"
npm run prepare

Add a hook:

npx husky add .husky/pre-commit "cd front && yarn lint-staged"

Edit package.json

"scripts": {
  "lint-staged": "lint-staged",
  "prepare": "cd .. && husky install front/.husky",
  "lint": "eslint --ignore-path .gitignore \"**/*.{js,jsx}\"",
  "lint:fix": "yarn lint --fix"
},
"lint-staged": {
  "*.{js,jsx,ts,tsx}": [
    "yarn lint:fix"
  ],
  "*.css": [
    "prettier --write"
  ]
}

For more detail you can see in typicode.github.io/husky/#/?id…

源码解析

source code

  1. bin.ts

运行 husky install 时调用了 insdex.ts 里的 instal 方法,该方法接收一个 dir 参数,允许通过命令行传入 .husky 的目录(当 .gitpackage.json 不在同一级目录时有用)。

export function install(dir = '.husky'): void {
  // Ensure that we're inside a git repository
  if (spawnSync('git', ['rev-parse']).status !== 0) {
    l('not a Git repository, skipping hooks installation')
    return
  }

  // Custom dir help
  const url = 'https://typicode.github.io/husky/#/?id=custom-directory'

  // Ensure that we're not trying to install outside of cwd
  if (!resolve(process.cwd(), dir).startsWith(process.cwd())) {
    throw new Error(`.. not allowed (see ${url})`)
  }

  // Ensure that cwd is git top level
  if (!existsSync('.git')) {
    throw new Error(`.git can't be found (see ${url})`)
  }

  try {
    // Create .husky/_
    mkdirSync(join(dir, '_'), { recursive: true })

    // Create .husky/.gitignore
    writeFileSync(join(dir, '.gitignore'), '_\n')

    // Copy husky.sh to .husky/_/husky.sh
    copyFileSync(
      fileURLToPath(new URL('./husky.sh', import.meta.url)),
      join(dir, '_/husky.sh'),
    )

    // Configure repo
    const { error } = spawnSync('git', ['config', 'core.hooksPath', dir])
    if (error) {
      throw error
    }
  } catch (e) {
    l('Git hooks failed to install')
    throw e
  }

  l('Git hooks installed')
}

做了几件事:

  • a. 确保当前在一个 git repository 里
  • b. 确保 dir 在当前目录里
  • c. 确保当前目录存在 .git 目录
  • d. 创建 .husky 目录,复制 husky.sh.husky/_/husky.sh,每次提交都会触发这个脚本,用于实时展示一些信息。
  • e. git config core.hooksPath 配置到 .husky,这样在 git commit 前就会触发 .husky/pre-commit,从而执行里面的脚本