11import fs from 'node:fs/promises' ;
22import path from 'node:path' ;
33import type { ESLint as ESLintClass } from 'eslint' ;
4+ import { lock } from './lock' ;
45import { makeDir } from './make-dir' ;
56import {
67 ClientGenerationResultFile ,
78 CommonOpenApiClientGeneratorConfigPostprocess
89} from '../schema-to-typescript/config' ;
910
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+
1025export async function postprocessFiles ( {
1126 files,
1227 config : { eslint : enableEslint } = { } ,
@@ -25,68 +40,72 @@ export async function postprocessFiles({
2540 directories . add ( directory ) ;
2641 }
2742
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 ) ;
4462 }
45- currentDirectory = path . join ( currentDirectory , subDirectory ) ;
4663 }
4764 }
48- }
65+ } ) ;
4966
67+ const eslint = getEslintInstance ( ) ;
5068 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-
5869 return await Promise . all (
5970 files . map ( async ( file ) => {
6071 const filePath = path . resolve ( outputDirPath , file . filename ) ;
61- let fileCreated = false ;
62- try {
72+ return await lock ( `file: ${ filePath } ` , async ( ) => {
73+ let fileCreated = false ;
6374 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 ) ;
7394 }
7495 }
75- return {
76- ...file ,
77- data : result . output ?? file . data
78- } ;
79- } finally {
80- if ( fileCreated ) {
81- await fs . unlink ( filePath ) ;
82- }
83- }
96+ } ) ;
8497 } )
8598 ) ;
8699 } 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+ } ) ;
90109 }
91110 }
92111 return files ;
0 commit comments