1
1
import { UsageError } from 'clipanion' ;
2
- import type { FileHandle } from 'fs/promises' ;
3
2
import fs from 'fs' ;
4
3
import path from 'path' ;
5
4
import process from 'process' ;
@@ -25,50 +24,58 @@ export type PackageManagerRequest = {
25
24
binaryVersion : string | null ;
26
25
} ;
27
26
28
- export function getLastKnownGoodFile ( flag = `r` ) {
29
- return fs . promises . open ( path . join ( folderUtils . getCorepackHomeFolder ( ) , `lastKnownGood.json` ) , flag ) ;
30
- }
31
- async function createLastKnownGoodFile ( ) {
32
- await fs . promises . mkdir ( folderUtils . getCorepackHomeFolder ( ) , { recursive : true } ) ;
33
- return getLastKnownGoodFile ( `w` ) ;
27
+ function getLastKnownGoodFilePath ( ) {
28
+ return path . join ( folderUtils . getCorepackHomeFolder ( ) , `lastKnownGood.json` ) ;
34
29
}
35
30
36
- export async function getJSONFileContent ( fh : FileHandle ) {
37
- let lastKnownGood : unknown ;
31
+ export async function getLastKnownGood ( ) : Promise < Record < string , string > > {
32
+ let raw : string ;
38
33
try {
39
- lastKnownGood = JSON . parse ( await fh . readFile ( `utf8` ) ) ;
34
+ raw = await fs . promises . readFile ( getLastKnownGoodFilePath ( ) , `utf8` ) ;
35
+ } catch ( err ) {
36
+ if ( ( err as NodeError ) ?. code === `ENOENT` ) return { } ;
37
+ throw err ;
38
+ }
39
+
40
+ try {
41
+ const parsed = JSON . parse ( raw ) ;
42
+ if ( ! parsed ) return { } ;
43
+ if ( typeof parsed !== `object` ) return { } ;
44
+ Object . entries ( parsed ) . forEach ( ( [ key , value ] ) => {
45
+ if ( typeof value !== `string` ) {
46
+ // Ensure that all entries are strings.
47
+ delete parsed [ key ] ;
48
+ }
49
+ } ) ;
50
+ return parsed ;
40
51
} catch {
41
52
// Ignore errors; too bad
42
- return undefined ;
53
+ return { } ;
43
54
}
44
-
45
- return lastKnownGood ;
46
55
}
47
56
48
- async function overwriteJSONFileContent ( fh : FileHandle , content : unknown ) {
49
- await fh . truncate ( 0 ) ;
50
- await fh . write ( `${ JSON . stringify ( content , null , 2 ) } \n` , 0 ) ;
57
+ async function createLastKnownGoodFile ( lastKnownGood : Record < string , string > ) {
58
+ const content = `${ JSON . stringify ( lastKnownGood , null , 2 ) } \n` ;
59
+ await fs . promises . mkdir ( folderUtils . getCorepackHomeFolder ( ) , { recursive : true } ) ;
60
+ await fs . promises . writeFile ( getLastKnownGoodFilePath ( ) , content , `utf8` ) ;
51
61
}
52
62
53
- export function getLastKnownGoodFromFileContent ( lastKnownGood : unknown , packageManager : string ) {
54
- if ( typeof lastKnownGood === `object` && lastKnownGood !== null &&
55
- Object . hasOwn ( lastKnownGood , packageManager ) ) {
56
- const override = ( lastKnownGood as any ) [ packageManager ] ;
57
- if ( typeof override === `string` ) {
58
- return override ;
59
- }
60
- }
63
+ export function getLastKnownGoodFromFileContent ( lastKnownGood : Record < string , string > , packageManager : string ) {
64
+ if ( Object . hasOwn ( lastKnownGood , packageManager ) )
65
+ return lastKnownGood [ packageManager ] ;
61
66
return undefined ;
62
67
}
63
68
64
- export async function activatePackageManagerFromFileHandle ( lastKnownGoodFile : FileHandle , lastKnownGood : unknown , locator : Locator ) {
65
- if ( typeof lastKnownGood !== `object` || lastKnownGood === null )
66
- lastKnownGood = { } ;
69
+ export async function activatePackageManager ( lastKnownGood : Record < string , string > , locator : Locator ) {
70
+ if ( lastKnownGood [ locator . name ] === locator . reference ) {
71
+ debugUtils . log ( `${ locator . name } @${ locator . reference } is already Last Known Good version` ) ;
72
+ return ;
73
+ }
67
74
68
- ( lastKnownGood as Record < string , string > ) [ locator . name ] = locator . reference ;
75
+ lastKnownGood [ locator . name ] = locator . reference ;
69
76
70
77
debugUtils . log ( `Setting ${ locator . name } @${ locator . reference } as Last Known Good version` ) ;
71
- await overwriteJSONFileContent ( lastKnownGoodFile , lastKnownGood ) ;
78
+ await createLastKnownGoodFile ( lastKnownGood ) ;
72
79
}
73
80
74
81
export class Engine {
@@ -150,54 +157,32 @@ export class Engine {
150
157
if ( typeof definition === `undefined` )
151
158
throw new UsageError ( `This package manager (${ packageManager } ) isn't supported by this corepack build` ) ;
152
159
153
- let lastKnownGoodFile = await getLastKnownGoodFile ( `r+` ) . catch ( err => {
154
- if ( ( err as NodeError ) ?. code !== `ENOENT` && ( err as NodeError ) ?. code !== `EROFS` ) {
155
- throw err ;
156
- }
157
- } ) ;
158
- try {
159
- const lastKnownGood = lastKnownGoodFile == null || await getJSONFileContent ( lastKnownGoodFile ! ) ;
160
- const lastKnownGoodForThisPackageManager = getLastKnownGoodFromFileContent ( lastKnownGood , packageManager ) ;
161
- if ( lastKnownGoodForThisPackageManager )
162
- return lastKnownGoodForThisPackageManager ;
163
-
164
- if ( process . env . COREPACK_DEFAULT_TO_LATEST === `0` )
165
- return definition . default ;
166
-
167
- const reference = await corepackUtils . fetchLatestStableVersion ( definition . fetchLatestFrom ) ;
168
-
169
- try {
170
- lastKnownGoodFile ??= await createLastKnownGoodFile ( ) ;
171
- await activatePackageManagerFromFileHandle ( lastKnownGoodFile , lastKnownGood , {
172
- name : packageManager ,
173
- reference,
174
- } ) ;
175
- } catch {
176
- // If for some reason, we cannot update the last known good file, we
177
- // ignore the error.
178
- }
160
+ const lastKnownGood = await getLastKnownGood ( ) ;
161
+ const lastKnownGoodForThisPackageManager = getLastKnownGoodFromFileContent ( lastKnownGood , packageManager ) ;
162
+ if ( lastKnownGoodForThisPackageManager )
163
+ return lastKnownGoodForThisPackageManager ;
179
164
180
- return reference ;
181
- } finally {
182
- await lastKnownGoodFile ?. close ( ) ;
183
- }
184
- }
165
+ if ( process . env . COREPACK_DEFAULT_TO_LATEST === `0` )
166
+ return definition . default ;
185
167
186
- async activatePackageManager ( locator : Locator ) {
187
- let emptyFile = false ;
188
- const lastKnownGoodFile = await getLastKnownGoodFile ( `r+` ) . catch ( err => {
189
- if ( ( err as NodeError ) ?. code === `ENOENT` ) {
190
- emptyFile = true ;
191
- return getLastKnownGoodFile ( `w` ) ;
192
- }
168
+ const reference = await corepackUtils . fetchLatestStableVersion ( definition . fetchLatestFrom ) ;
193
169
194
- throw err ;
195
- } ) ;
196
170
try {
197
- await activatePackageManagerFromFileHandle ( lastKnownGoodFile , emptyFile || await getJSONFileContent ( lastKnownGoodFile ) , locator ) ;
198
- } finally {
199
- await lastKnownGoodFile . close ( ) ;
171
+ await activatePackageManager ( lastKnownGood , {
172
+ name : packageManager ,
173
+ reference,
174
+ } ) ;
175
+ } catch {
176
+ // If for some reason, we cannot update the last known good file, we
177
+ // ignore the error.
200
178
}
179
+
180
+ return reference ;
181
+ }
182
+
183
+ async activatePackageManager ( locator : Locator ) {
184
+ const lastKnownGood = await getLastKnownGood ( ) ;
185
+ await activatePackageManager ( lastKnownGood , locator ) ;
201
186
}
202
187
203
188
async ensurePackageManager ( locator : Locator ) {
0 commit comments