hookem
Version:
`hookem` turns simple simple CLI commands into git hooks. It's similar to Husky version 4, but even more minimal.
119 lines (88 loc) • 2.41 kB
JavaScript
import { existsSync, promises as fs } from 'fs';
import { join } from 'path';
import { debuglog } from 'util';
import { findUp } from 'find-up';
import hasYarn from 'has-yarn';
import { readPackageUp } from 'read-pkg-up';
const debug = debuglog('hookem');
const useYarn = hasYarn();
const isHook = (contents) => contents.includes('# hookem');
async function install() {
const [{ packageJson }, gitDir] = await Promise.all([
readPackageUp(),
findUp('.git', { type: 'directory' }),
]);
if (!gitDir) {
debug('Not a git repo');
return false;
}
const hooks = packageJson?.gitHooks || packageJson?.husky?.hooks;
if (!hooks) {
debug('No hooks configured');
return false;
}
const hookDir = join(gitDir, 'hooks');
if (!existsSync(hookDir)) {
await fs.mkdir(hookDir);
}
return Promise.all(
Object.entries(hooks).map(async ([hook, cmd]) => {
const command = useYarn ? `yarn run ${cmd}` : `npx --no-install ${cmd}`;
const filename = join(hookDir, hook);
if (existsSync(filename)) {
const data = await fs.readFile(filename, 'utf-8');
if (!isHook(data)) {
return;
}
}
const failMessage = [
'commit-msg',
'pre-commit',
'pre-rebase',
'pre-push',
].includes(hook)
? '(add --no-verify to bypass)'
: '(cannot be bypassed with --no-verify due to Git specs)';
await fs.writeFile(
filename,
`#!/bin/sh
# hookem
echo "Hook'em -> ${hook}"
cmd="${command}"
$cmd
status=$?
if [ $status != 0 ]; then
echo "${failMessage}";
fi
exit $status;`,
);
await fs.chmod(filename, 0o0755);
}),
).then(() => true);
}
async function canRemove(filename) {
const stat = await fs.stat(filename);
if (stat.isFile()) {
const data = await fs.readFile(filename, 'utf-8');
return isHook(data);
}
return false;
}
async function uninstall() {
const gitDir = await findUp('.git', { type: 'directory' });
if (!gitDir) return null;
const hookDir = join(gitDir, 'hooks');
if (!existsSync(hookDir)) {
return null;
}
const files = await fs.readdir(hookDir);
return Promise.all(
files.map(async (hookName) => {
const filename = join(hookDir, hookName);
if (await canRemove(filename)) {
await fs.unlink(filename);
}
}),
);
}
export { install, uninstall };