-
Notifications
You must be signed in to change notification settings - Fork 1
/
githook.ts
86 lines (79 loc) · 2.41 KB
/
githook.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import { Rule, TaskPayload } from '../counsel' // eslint-disable-line no-unused-vars
export type Hooks =
| 'applypatch-msg'
| 'commit-msg'
| 'fsmonitor-watchman'
| 'post-update'
| 'pre-applypatch'
| 'pre-commit'
| 'pre-push'
| 'pre-rebase'
| 'pre-receive'
| 'prepare-commit-msg'
| 'update'
export type HooksMap = Partial<{ [K in Hooks]: string }>
export interface GitHooksRule extends Rule {
hooks: HooksMap
strict?: boolean
}
/**
* create a GitHooksRule with a prebound check, plan, and dependencies
*/
export const create: (
partialRule: Partial<GitHooksRule> & {
name: string
hooks: GitHooksRule['hooks']
}
) => GitHooksRule = partialRule => ({
check,
plan,
...partialRule,
devDependencies: [
{ name: 'husky', range: '*' },
...(partialRule.dependencies || [])
]
})
export const getCurrentHooks = (packageJson: any) =>
(packageJson.husky && packageJson.husky.hooks) || {}
export const plan = (opts: TaskPayload<GitHooksRule>) => {
const {
ctx: { packageJson },
rule: { hooks: desiredHooksByName }
} = opts
const currentHuskyHooks: Partial<{ [K in Hooks]: string }> = getCurrentHooks(
packageJson
)
const currentHuskyHookNames = Object.keys(currentHuskyHooks) as Hooks[]
const hookNamesToAdd = Object.keys(desiredHooksByName).filter(
desired => !currentHuskyHookNames.some(current => current === desired)
) as Hooks[]
if (!hookNamesToAdd.length) return null
return () => {
// upsert husky hooks container
packageJson.husky = packageJson.husky || {}
packageJson.husky.hooks = packageJson.husky.hooks || {}
for (const desiredHookName of hookNamesToAdd) {
packageJson.husky.hooks[desiredHookName] =
desiredHooksByName[desiredHookName]
}
}
}
export const check = (opts: TaskPayload<GitHooksRule>) => {
const {
ctx: { packageJson },
rule: { hooks: desiredHooksByName, strict }
} = opts
const currentHooksByName = getCurrentHooks(packageJson)
for (const hookName in desiredHooksByName) {
const currentHookValue = currentHooksByName[hookName]
const desiredHookValue = desiredHooksByName[hookName as Hooks]
if (strict && currentHookValue !== desiredHookValue) {
throw new Error(
`hook ${hookName} has different current and target values in strict mode`
)
}
if (!(hookName in currentHooksByName)) {
throw new Error(`githook "${hookName}" missing from current hooks`)
}
}
}