@@ -125,27 +125,9 @@ interface Hook {
125125 runAfter ?: HookName [ ] ;
126126}
127127
128- interface LockableHook extends Hook {
129- /** Upon resolution, indicates that this hook has completed */
130- lock : Promise < unknown > ;
131- /** Promise that must resolve before this hook begins execution */
132- locksToWaitFor ?: Promise < unknown > ;
133- /** Marks this hook as complete */
134- unlock : ( ) => void ;
135- }
136-
137- /** Wraps a non-lockable hook to add properties used for locking */
138- const createLockableHook = ( hook : Hook ) : LockableHook => {
139- let unlock = ( ) => undefined as void ;
140- const lock = new Promise < void > ( resolve => {
141- unlock = resolve ;
142- } ) ;
143- return { ...hook , lock, unlock } ;
144- } ;
145-
146128/** Hooks expressed in a format similar to .pre-commit-config.yaml */
147- const HOOKS : Record < HookName , LockableHook > = {
148- [ HookName . Autoflake ] : createLockableHook ( {
129+ const HOOKS : Record < HookName , Hook > = {
130+ [ HookName . Autoflake ] : {
149131 action : sources =>
150132 run (
151133 "autoflake" ,
@@ -158,8 +140,8 @@ const HOOKS: Record<HookName, LockableHook> = {
158140 ) ,
159141 include : / \. p y $ / ,
160142 runAfter : [ HookName . Sed ] ,
161- } ) ,
162- [ HookName . Black ] : createLockableHook ( {
143+ } ,
144+ [ HookName . Black ] : {
163145 action : async ( sources , args ) =>
164146 args [ "python-version" ] ?. startsWith ( "2" ) &&
165147 run (
@@ -180,8 +162,8 @@ const HOOKS: Record<HookName, LockableHook> = {
180162 ) ,
181163 include : / \. p y $ / ,
182164 runAfter : [ HookName . Isort ] ,
183- } ) ,
184- [ HookName . ClangFormat ] : createLockableHook ( {
165+ } ,
166+ [ HookName . ClangFormat ] : {
185167 action : sources =>
186168 run (
187169 "/clang-format" ,
@@ -191,8 +173,8 @@ const HOOKS: Record<HookName, LockableHook> = {
191173 ) ,
192174 include : / \. ( c p p | p r o t o $ ) / ,
193175 runAfter : [ HookName . Sed ] ,
194- } ) ,
195- [ HookName . EsLint ] : createLockableHook ( {
176+ } ,
177+ [ HookName . EsLint ] : {
196178 action : async sources => {
197179 try {
198180 await run (
@@ -210,25 +192,25 @@ const HOOKS: Record<HookName, LockableHook> = {
210192 exclude : MINIFIED_JS_REGEX ,
211193 include : / \. [ j t ] s x ? $ / ,
212194 runAfter : [ HookName . Sed ] ,
213- } ) ,
214- [ HookName . Gofmt ] : createLockableHook ( {
195+ } ,
196+ [ HookName . Gofmt ] : {
215197 action : sources => run ( "/gofmt" , "-s" , "-w" , ...sources ) ,
216198 include : / \. g o $ / ,
217199 runAfter : [ HookName . Sed ] ,
218- } ) ,
219- [ HookName . GoogleJavaFormat ] : createLockableHook ( {
200+ } ,
201+ [ HookName . GoogleJavaFormat ] : {
220202 action : sources =>
221203 run ( "java" , "-jar" , "/google-java-format" , "--replace" , ...sources ) ,
222204 include : / \. j a v a $ / ,
223205 runAfter : [ HookName . Sed ] ,
224- } ) ,
225- [ HookName . GradleDependenciesSorter ] : createLockableHook ( {
206+ } ,
207+ [ HookName . GradleDependenciesSorter ] : {
226208 action : sources =>
227209 run ( "java" , "-jar" , "/gradle-dependencies-sorter" , ...sources ) ,
228210 include : / b u i l d \. g r a d l e ( \. k t s ) ? $ / ,
229211 runAfter : [ HookName . Sed ] ,
230- } ) ,
231- [ HookName . Isort ] : createLockableHook ( {
212+ } ,
213+ [ HookName . Isort ] : {
232214 // isort's automatic config file detection is broken
233215 // https://github.com/PyCQA/isort/issues/1907
234216 // https://github.com/samueljsb/qaz/pull/104
@@ -237,8 +219,8 @@ const HOOKS: Record<HookName, LockableHook> = {
237219 run ( "isort" , "--settings" , "/.editorconfig" , ...sources ) ,
238220 include : / \. p y $ / ,
239221 runAfter : [ HookName . Autoflake ] ,
240- } ) ,
241- [ HookName . Ktfmt ] : createLockableHook ( {
222+ } ,
223+ [ HookName . Ktfmt ] : {
242224 action : async sources => {
243225 /** Try to avoid ktfmt OOMs presumably caused by too many input files */
244226 const MAX_FILES_PER_PROCESS = 200 ;
@@ -261,13 +243,13 @@ const HOOKS: Record<HookName, LockableHook> = {
261243 } ,
262244 include : / \. k t s ? $ / ,
263245 runAfter : [ HookName . Sed ] ,
264- } ) ,
265- [ HookName . PackerFmt ] : createLockableHook ( {
246+ } ,
247+ [ HookName . PackerFmt ] : {
266248 action : sources => run ( "/packer" , "fmt" , ...sources ) ,
267249 include : / \. p k r \. h c l $ / ,
268250 runAfter : [ HookName . Sed ] ,
269- } ) ,
270- [ HookName . PrettierJs ] : createLockableHook ( {
251+ } ,
252+ [ HookName . PrettierJs ] : {
271253 action : sources =>
272254 run (
273255 "prettier" ,
@@ -279,8 +261,8 @@ const HOOKS: Record<HookName, LockableHook> = {
279261 exclude : MINIFIED_JS_REGEX ,
280262 include : / \. j s x ? $ / ,
281263 runAfter : [ HookName . Sed , HookName . EsLint ] ,
282- } ) ,
283- [ HookName . PrettierNonJs ] : createLockableHook ( {
264+ } ,
265+ [ HookName . PrettierNonJs ] : {
284266 action : sources =>
285267 run (
286268 "prettier" ,
@@ -292,8 +274,8 @@ const HOOKS: Record<HookName, LockableHook> = {
292274 ) ,
293275 include : / \. ( c s s | h t m l ? | m a r k d o w n | m d | s c s s | t s x ? | x m l | y a ? m l ) $ / ,
294276 runAfter : [ HookName . Sed , HookName . Xsltproc , HookName . EsLint ] ,
295- } ) ,
296- [ HookName . Ruff ] : createLockableHook ( {
277+ } ,
278+ [ HookName . Ruff ] : {
297279 action : async ( sources , args ) => {
298280 if ( args [ "python-version" ] ?. startsWith ( "2" ) ) {
299281 return ;
@@ -306,8 +288,8 @@ const HOOKS: Record<HookName, LockableHook> = {
306288 } ,
307289 include : / \. p y $ / ,
308290 runAfter : [ HookName . Autoflake ] ,
309- } ) ,
310- [ HookName . Scalafmt ] : createLockableHook ( {
291+ } ,
292+ [ HookName . Scalafmt ] : {
311293 action : async ( sources , args ) =>
312294 run (
313295 "/scalafmt" ,
@@ -330,12 +312,12 @@ const HOOKS: Record<HookName, LockableHook> = {
330312 ) ,
331313 include : / \. ( s c a l a | s b t | s c ) $ / ,
332314 runAfter : [ HookName . Sed ] ,
333- } ) ,
315+ } ,
334316 // Mimic sed by applying arbitrary regex transformations. Before proposing a
335317 // new transformation, please make sure that it's both (1) safe and (2) likely
336318 // to ever actually be needed. At Duolingo, we determine the latter criterion
337319 // empirically by seeing how many existing violations our codebase contains
338- [ HookName . Sed ] : createLockableHook ( {
320+ [ HookName . Sed ] : {
339321 action : ( sources , args ) =>
340322 Promise . all (
341323 sources . map ( source =>
@@ -391,8 +373,8 @@ const HOOKS: Record<HookName, LockableHook> = {
391373 ) ,
392374 ) ,
393375 include : / ./ ,
394- } ) ,
395- [ HookName . Shfmt ] : createLockableHook ( {
376+ } ,
377+ [ HookName . Shfmt ] : {
396378 action : async sources => {
397379 // Find source files that are Shell files. shfmt has a `-f` flag that
398380 // does this, but it sometimes returns false positives
@@ -442,13 +424,13 @@ const HOOKS: Record<HookName, LockableHook> = {
442424 exclude : / \. p r o t o $ / ,
443425 include : / ./ ,
444426 runAfter : [ HookName . Sed ] ,
445- } ) ,
446- [ HookName . Svgo ] : createLockableHook ( {
427+ } ,
428+ [ HookName . Svgo ] : {
447429 action : sources => run ( "svgo" , "--config" , "/svgo.config.js" , ...sources ) ,
448430 include : / \. s v g $ / ,
449431 runAfter : [ HookName . Sed ] ,
450- } ) ,
451- [ HookName . Taplo ] : createLockableHook ( {
432+ } ,
433+ [ HookName . Taplo ] : {
452434 action : sources =>
453435 run (
454436 "/taplo" ,
@@ -467,8 +449,8 @@ const HOOKS: Record<HookName, LockableHook> = {
467449 ) ,
468450 include : / \. t o m l $ / ,
469451 runAfter : [ HookName . Sed ] ,
470- } ) ,
471- [ HookName . TerraformFmt ] : createLockableHook ( {
452+ } ,
453+ [ HookName . TerraformFmt ] : {
472454 action : sources =>
473455 Promise . all (
474456 // Officially `terraform fmt` only accepts a directory to recurse
@@ -484,8 +466,8 @@ const HOOKS: Record<HookName, LockableHook> = {
484466 ) ,
485467 include : / \. t f $ / ,
486468 runAfter : [ HookName . Sed ] ,
487- } ) ,
488- [ HookName . Xsltproc ] : createLockableHook ( {
469+ } ,
470+ [ HookName . Xsltproc ] : {
489471 action : sources =>
490472 Promise . all (
491473 sources . map ( source =>
@@ -494,7 +476,7 @@ const HOOKS: Record<HookName, LockableHook> = {
494476 ) ,
495477 include : / \. x m l $ / ,
496478 runAfter : [ HookName . Sed ] ,
497- } ) ,
479+ } ,
498480} ;
499481
500482/** Files that match this pattern should never be processed */
@@ -550,19 +532,36 @@ const prefixLines = (() => {
550532 }
551533 }
552534
553- // Set up hook locks
554- Object . values ( HOOKS ) . forEach ( hook => {
535+ // Augment hook definitions to make them lockable
536+ interface LockableHook extends Hook {
537+ /** Upon resolution, indicates that this hook has completed */
538+ lock: Promise < unknown > ;
539+ /** Promise that must resolve before this hook begins execution */
540+ locksToWaitFor ?: Promise < unknown > ;
541+ /** Marks this hook as complete */
542+ unlock : ( ) => void ;
543+ }
544+ const lockableHooks = Object . fromEntries (
545+ Object . entries ( HOOKS ) . map ( ( [ name , hook ] ) => {
546+ let unlock = ( ) => undefined as void ;
547+ const lock = new Promise < void > ( resolve => {
548+ unlock = resolve ;
549+ } ) ;
550+ return [ name , { ...hook , lock, unlock } ] ;
551+ } ) ,
552+ ) as Record < HookName , LockableHook > ;
553+ Object . values ( lockableHooks ) . forEach ( hook => {
555554 if ( hook . runAfter ) {
556555 hook . locksToWaitFor = Promise . all (
557- hook . runAfter . map ( hookName => HOOKS [ hookName ] . lock ) ,
556+ hook . runAfter . map ( hookName => lockableHooks [ hookName ] . lock ) ,
558557 ) ;
559558 }
560559 } ) ;
561560
562561 // Run all hooks in parallel
563562 let success = true ;
564563 await Promise . all (
565- Object . entries ( HOOKS ) . map (
564+ Object . entries ( lockableHooks ) . map (
566565 async ( [ name , { action, exclude, include, locksToWaitFor, unlock } ] ) => {
567566 // Wait until necessary hooks have completed
568567 await locksToWaitFor ;
0 commit comments