[toc]

# 1. 开始

v7+版本的husky原理太简单了,这里记录下。

# 2. 原理

简单来说,就是把git的钩子目录指定到了项目根目录中.husky文件夹下,默认情况下是.git/hooks下,其使用的命令是:

const git = (args) => cp.spawnSync('git', args, { stdio: 'inherit' });
git(['config', 'core.hooksPath', dir]);

这样的话,就可以在git钩子中进行commit信息检查、lint检查等。

常见的git钩子有:

  • commit-msg
  • pre-commit
  • post-commit
  • pre-push

有一点要注意,husky install执行后,写在.husky中的git钩子才生效。如果你先执行了npm install,然后从被人项目拷贝过来husky install,但从未执行,那钩子是不会生效的。

package.json中的prepare命令会在npm install完成后执行。

# 3. 源码

核心源码只有两个文件:

- bin.ts
- index.ts

总共暴露出4个命令:

const cmds: { [key: string]: () => void } = {
  install: (): void => (ln > 1 ? help(2) : h.install(x)),
  uninstall: h.uninstall,
  set: hook(h.set),
  add: hook(h.add),
  ['-v']: () =>
    console.log(require(p.join(__dirname, '../package.json')).version),
}

husky install上面已经讲过了,uninstall是相反的命令:

git(['config', '--unset', 'core.hooksPath'])

setadd都是添加git钩子中的指令,set可以新增指令文件,add可以增加指令。

export function set(file: string, cmd: string): void {
  const dir = p.dirname(file)
  if (!fs.existsSync(dir)) {
    throw new Error(
      `can't create hook, ${dir} directory doesn't exist (try running husky install)`,
    )
  }

  fs.writeFileSync(
    file,
    `#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

${cmd}
`,
    { mode: 0o0755 },
  )

  l(`created ${file}`)
}
export function add(file: string, cmd: string): void {
  if (fs.existsSync(file)) {
    fs.appendFileSync(file, `${cmd}\n`)
    l(`updated ${file}`)
  } else {
    set(file, cmd)
  }
}

# 4. 结语

husky的原理介绍到这就结束了,是不是很简单?

有兴趣的可以自己读下源码 (opens new window),说不定有其他收获。