1
1
import fs from 'node:fs/promises' ;
2
2
import path from 'node:path' ;
3
3
import type { ESLint as ESLintClass } from 'eslint' ;
4
+ import { lock } from './lock' ;
4
5
import { makeDir } from './make-dir' ;
5
6
import {
6
7
ClientGenerationResultFile ,
7
8
CommonOpenApiClientGeneratorConfigPostprocess
8
9
} from '../schema-to-typescript/config' ;
9
10
11
+ let eslintInstance : ESLintClass | null = null ;
12
+
13
+ function getEslintInstance ( ) : ESLintClass {
14
+ if ( eslintInstance === null ) {
15
+ // This is an optional dependency, so we require it here to avoid loading it when it's not needed.
16
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
17
+ const { ESLint} = require ( 'eslint' ) as { ESLint : typeof ESLintClass } ;
18
+ eslintInstance = new ESLint ( {
19
+ fix : true
20
+ } ) ;
21
+ }
22
+ return eslintInstance ;
23
+ }
24
+
10
25
export async function postprocessFiles ( {
11
26
files,
12
27
config : { eslint : enableEslint } = { } ,
@@ -25,68 +40,72 @@ export async function postprocessFiles({
25
40
directories . add ( directory ) ;
26
41
}
27
42
28
- for ( const directory of Array . from ( directories ) ) {
29
- try {
30
- await fs . stat ( directory ) ;
31
- } catch ( _e ) {
32
- const directoryBits = directory . split ( path . sep ) ;
33
- let currentDirectory = directoryBits . shift ( ) || '/' ;
34
- for ( ; ; ) {
35
- try {
36
- await fs . stat ( currentDirectory ) ;
37
- } catch ( e ) {
38
- await makeDir ( currentDirectory ) ;
39
- directoriesToRemove . unshift ( currentDirectory ) ;
40
- }
41
- const subDirectory = directoryBits . shift ( ) ;
42
- if ( ! subDirectory ) {
43
- break ;
43
+ await lock ( 'calc:directories' , async ( ) => {
44
+ for ( const directory of Array . from ( directories ) ) {
45
+ try {
46
+ await fs . stat ( directory ) ;
47
+ } catch ( _e ) {
48
+ const directoryBits = directory . split ( path . sep ) ;
49
+ let currentDirectory = directoryBits . shift ( ) || '/' ;
50
+ for ( ; ; ) {
51
+ try {
52
+ await fs . stat ( currentDirectory ) ;
53
+ } catch ( e ) {
54
+ await makeDir ( currentDirectory ) ;
55
+ directoriesToRemove . unshift ( currentDirectory ) ;
56
+ }
57
+ const subDirectory = directoryBits . shift ( ) ;
58
+ if ( ! subDirectory ) {
59
+ break ;
60
+ }
61
+ currentDirectory = path . join ( currentDirectory , subDirectory ) ;
44
62
}
45
- currentDirectory = path . join ( currentDirectory , subDirectory ) ;
46
63
}
47
64
}
48
- }
65
+ } ) ;
49
66
67
+ const eslint = getEslintInstance ( ) ;
50
68
try {
51
- // This is an optional dependency, so we require it here to avoid loading it when it's not needed.
52
- // eslint-disable-next-line @typescript-eslint/no-var-requires
53
- const { ESLint} = require ( 'eslint' ) as { ESLint : typeof ESLintClass } ;
54
- const eslint = new ESLint ( {
55
- fix : true
56
- } ) ;
57
-
58
69
return await Promise . all (
59
70
files . map ( async ( file ) => {
60
71
const filePath = path . resolve ( outputDirPath , file . filename ) ;
61
- let fileCreated = false ;
62
- try {
72
+ return await lock ( `file: ${ filePath } ` , async ( ) => {
73
+ let fileCreated = false ;
63
74
try {
64
- await fs . stat ( filePath ) ;
65
- } catch ( _e ) {
66
- await fs . writeFile ( filePath , file . data ) ;
67
- fileCreated = true ;
68
- }
69
- const [ result ] = await eslint . lintText ( file . data , { filePath} ) ;
70
- for ( const message of result . messages ) {
71
- if ( message . fatal ) {
72
- throw new Error ( `Fatal ESLint error in ${ file . filename } : ${ message . message } ` ) ;
75
+ try {
76
+ await fs . stat ( filePath ) ;
77
+ } catch ( _e ) {
78
+ await fs . writeFile ( filePath , file . data ) ;
79
+ fileCreated = true ;
80
+ }
81
+ const [ result ] = await eslint . lintText ( file . data , { filePath} ) ;
82
+ for ( const message of result . messages ) {
83
+ if ( message . fatal ) {
84
+ throw new Error ( `Fatal ESLint error in ${ file . filename } : ${ message . message } ` ) ;
85
+ }
86
+ }
87
+ return {
88
+ ...file ,
89
+ data : result . output ?? file . data
90
+ } ;
91
+ } finally {
92
+ if ( fileCreated ) {
93
+ await fs . unlink ( filePath ) ;
73
94
}
74
95
}
75
- return {
76
- ...file ,
77
- data : result . output ?? file . data
78
- } ;
79
- } finally {
80
- if ( fileCreated ) {
81
- await fs . unlink ( filePath ) ;
82
- }
83
- }
96
+ } ) ;
84
97
} )
85
98
) ;
86
99
} finally {
87
- for ( const directory of directoriesToRemove ) {
88
- await fs . rmdir ( directory ) ;
89
- }
100
+ await lock ( 'cleanup:directories' , async ( ) => {
101
+ for ( const directory of directoriesToRemove ) {
102
+ try {
103
+ await fs . rmdir ( directory ) ;
104
+ } catch ( e ) {
105
+ // Ignore
106
+ }
107
+ }
108
+ } ) ;
90
109
}
91
110
}
92
111
return files ;
0 commit comments