@@ -3,6 +3,9 @@ import { Document, ParsedNode } from "yaml";
3
3
import { Context } from "./Context" ;
4
4
import { getPackageDeps } from "./getPackageDeps" ;
5
5
6
+ /**
7
+ * Subset of the .pre-commit-config.yaml schema that we care about.
8
+ */
6
9
interface PreCommitConfig {
7
10
repos : {
8
11
hooks : {
@@ -14,15 +17,18 @@ interface PreCommitConfig {
14
17
}
15
18
16
19
/**
17
- * Given a tsconfig.json , update it to match our conventions. This function is
18
- * called by the pnpm `meta-updater` plugin either to check if the tsconfig.json
19
- * is up to date or to update it, depending on flags.
20
+ * Given a .pre-commit-config.yaml , update it to match our conventions. This
21
+ * function is called by the pnpm `meta-updater` plugin either to check if the
22
+ * .pre-commit-config.yaml is up to date or to update it, depending on flags.
20
23
* @param context Contains context such as workspace dir and parsed pnpm
21
24
* lockfile
22
- * @param rawInput The input tsconfig.json that should be checked / updated
25
+ * @param rawInput The input .pre-commit-config.yaml that should be checked /
26
+ * updated. This is a parsed yaml document in the `yaml` library's document
27
+ * representation; not a plain js object like you'd get from a json parser. We
28
+ * need it like this so that we can preserve comments.
23
29
* @param options Extra information provided by pnpm; mostly just the directory
24
- * of the package whose tsconfig.json we are updating
25
- * @returns The updated tsconfig.json
30
+ * of the package whose .pre-commit-config.yaml we are updating
31
+ * @returns The updated .pre-commit-config.yaml
26
32
*/
27
33
export async function updatePreCommit (
28
34
{ workspaceDir, pnpmLockfile } : Context ,
@@ -32,36 +38,73 @@ export async function updatePreCommit(
32
38
if ( rawInput == null ) {
33
39
return null ;
34
40
}
35
- /** Directory of the package whose tsconfig.json we are updating */
41
+ /** Directory of the package whose .pre-commit-config.yaml we are updating */
36
42
const packageDir = options . dir ;
37
43
38
44
if ( packageDir !== workspaceDir ) {
39
45
throw new Error ( "updatePreCommit should only be called on root" ) ;
40
46
}
41
47
42
48
const deps = getPackageDeps ( workspaceDir , packageDir , pnpmLockfile ) ;
43
- const prettierVersion = deps [ "prettier" ] ;
44
49
45
- const prettierHookIndex = ( rawInput . toJS ( ) as PreCommitConfig ) . repos
50
+ updateHook ( deps , rawInput , "prettier" , ( name ) => name === "prettier" ) ;
51
+
52
+ return rawInput ;
53
+ }
54
+
55
+ /**
56
+ * Updates the additional_dependencies of a hook in a .pre-commit-config.yaml to
57
+ * match the versions from the lockfile.
58
+ * @param deps Dependencies of the package whose .pre-commit-config.yaml we are
59
+ * updating
60
+ * @param rawInput The input .pre-commit-config.yaml that should be checked /
61
+ * updated
62
+ * @param hookId The id of the hook to update
63
+ * @param packageMatcher A function that returns true if the given package name
64
+ * should be added to the hook's additional_dependencies
65
+ */
66
+ function updateHook (
67
+ deps : { [ x : string ] : string } ,
68
+ rawInput : Document < ParsedNode > ,
69
+ hookId : string ,
70
+ packageMatcher : ( name : string ) => boolean ,
71
+ ) {
72
+ const packages = Object . entries ( deps ) . filter ( ( [ name ] ) =>
73
+ packageMatcher ( name ) ,
74
+ ) ;
75
+
76
+ // Find the hook in the .pre-commit-config.yaml. Easier to grab the indices
77
+ // form the raw js representation so that we can just use `setIn` to update
78
+ // the hook
79
+ const desiredHook = ( rawInput . toJS ( ) as PreCommitConfig ) . repos
46
80
. flatMap ( ( { hooks } , repoIndex ) =>
47
81
hooks . map ( ( hook , hookIndex ) => ( { hook, repoIndex, hookIndex } ) ) ,
48
82
)
49
- . filter ( ( { hook } ) => hook . id === "prettier" ) ;
83
+ . filter ( ( { hook } ) => hook . id === hookId ) ;
50
84
51
- if ( prettierHookIndex . length === 0 ) {
52
- throw new Error ( " No prettier hook found" ) ;
85
+ if ( desiredHook . length === 0 ) {
86
+ throw new Error ( ` No ${ hookId } hook found` ) ;
53
87
}
54
88
55
- if ( prettierHookIndex . length > 1 ) {
56
- throw new Error ( " Multiple prettier hooks found" ) ;
89
+ if ( desiredHook . length > 1 ) {
90
+ throw new Error ( ` Multiple ${ hookId } hooks found` ) ;
57
91
}
58
92
59
- const { repoIndex, hookIndex } = prettierHookIndex [ 0 ] ;
93
+ const { repoIndex, hookIndex } = desiredHook [ 0 ] ;
60
94
61
95
rawInput . setIn (
62
96
[ "repos" , repoIndex , "hooks" , hookIndex , "additional_dependencies" ] ,
63
- rawInput . createNode ( [ `prettier@${ prettierVersion } ` ] ) ,
97
+ rawInput . createNode (
98
+ packages
99
+ . map ( ( [ name , version ] ) => {
100
+ if ( version . includes ( "(" ) ) {
101
+ // pnpm includes the integrity hash in the version, which we don't
102
+ // need here
103
+ version = version . slice ( 0 , version . indexOf ( "(" ) ) ;
104
+ }
105
+ return `${ name } @${ version } ` ;
106
+ } )
107
+ . sort ( ) ,
108
+ ) ,
64
109
) ;
65
-
66
- return rawInput ;
67
110
}
0 commit comments