11import { UsageError } from 'clipanion' ;
2- import type { FileHandle } from 'fs/promises' ;
32import fs from 'fs' ;
43import path from 'path' ;
54import process from 'process' ;
@@ -25,50 +24,58 @@ export type PackageManagerRequest = {
2524 binaryVersion : string | null ;
2625} ;
2726
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` ) ;
3429}
3530
36- export async function getJSONFileContent ( fh : FileHandle ) {
37- let lastKnownGood : unknown ;
31+ export async function getLastKnownGood ( ) : Promise < Record < string , string > > {
32+ let raw : string ;
3833 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 ;
4051 } catch {
4152 // Ignore errors; too bad
42- return undefined ;
53+ return { } ;
4354 }
44-
45- return lastKnownGood ;
4655}
4756
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` ) ;
5161}
5262
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 ] ;
6166 return undefined ;
6267}
6368
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+ }
6774
68- ( lastKnownGood as Record < string , string > ) [ locator . name ] = locator . reference ;
75+ lastKnownGood [ locator . name ] = locator . reference ;
6976
7077 debugUtils . log ( `Setting ${ locator . name } @${ locator . reference } as Last Known Good version` ) ;
71- await overwriteJSONFileContent ( lastKnownGoodFile , lastKnownGood ) ;
78+ await createLastKnownGoodFile ( lastKnownGood ) ;
7279}
7380
7481export class Engine {
@@ -150,54 +157,32 @@ export class Engine {
150157 if ( typeof definition === `undefined` )
151158 throw new UsageError ( `This package manager (${ packageManager } ) isn't supported by this corepack build` ) ;
152159
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 ;
179164
180- return reference ;
181- } finally {
182- await lastKnownGoodFile ?. close ( ) ;
183- }
184- }
165+ if ( process . env . COREPACK_DEFAULT_TO_LATEST === `0` )
166+ return definition . default ;
185167
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 ) ;
193169
194- throw err ;
195- } ) ;
196170 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.
200178 }
179+
180+ return reference ;
181+ }
182+
183+ async activatePackageManager ( locator : Locator ) {
184+ const lastKnownGood = await getLastKnownGood ( ) ;
185+ await activatePackageManager ( lastKnownGood , locator ) ;
201186 }
202187
203188 async ensurePackageManager ( locator : Locator ) {
0 commit comments