import path from 'path' ; import fs from 'fs' ; type Loader = ( ) => Promise < any > ; const requests = { } ; const errors = { } ; export const loadCache = createLoadCache ( 'presta-load-cache' ) ; function writeFileCache ( filepath , json ) { fs . writeFileSync ( filepath , JSON . stringify ( json ) , 'utf-8' ) ; } function readFileCache ( filepath ) { if ( ! fs . existsSync ( filepath ) ) fs . writeFileSync ( filepath , '{}' , 'utf-8' ) ; return JSON . parse ( fs . readFileSync ( filepath , 'utf8' ) ) ; } export function createLoadCache ( name , { dir = process . cwd ( ) } = { } ) { const filename = '.' + name ; const filepath = path . join ( dir , filename ) ; let cache = readFileCache ( filepath ) ; return { get ( key ) { const [ value , expiration ] = cache [ key ] || [ ] ; if ( expiration !== null && Date . now ( ) > expiration ) { delete cache [ key ] ; writeFileCache ( filepath , cache ) ; return undefined ; } else { return value ; } } , set ( key , value , duration ? ) { const expiration = duration ? Date . now ( ) + duration : null ; cache [ key ] = [ value , expiration ] ; if ( expiration ) writeFileCache ( filepath , cache ) ; } , clear ( key ) { delete cache [ key ] ; writeFileCache ( filepath , cache ) ; } , clearAllMemory ( ) { for ( const key of Object . keys ( cache ) ) { const [ value , expiration ] = cache [ key ] || [ ] ; if ( ! expiration ) delete cache [ key ] ; } } , cleanup ( ) { cache = { } ; try { fs . unlinkSync ( filepath ) ; } catch ( e ) { } } , dump ( ) { const res = { } ; for ( const key of Object . keys ( cache ) ) { res [ key ] = cache [ key ] [ 0 ] ; } return res ; } , } ; } export function loadError ( key , e ) { if ( ! process . env . TESTING ) console . error ( e ) ; errors [ key ] = e ; delete requests [ key ] ; } export function prime ( key , value , duration ? ) { loadCache . set ( key , value , duration ) ; } export async function cache ( loader , { key , duration } ) { let value = loadCache . get ( key ) ; if ( ! value ) { value = await loader ( ) ; loadCache . set ( key , value , duration ) ; } return value ; } export function load ( loader , { key , duration } ) { let value = loadCache . get ( key ) ; if ( ! value && ! errors [ key ] ) { try { requests [ key ] = loader ( ) ; requests [ key ] . then ( ( value ) => { loadCache . set ( key , value , duration ) ; delete requests [ key ] ; } ) . catch ( ( e ) => loadError ( key , e ) ) ; } catch ( e ) { loadError ( key , e as Error ) ; } } delete errors [ key ] ; return value ; } export async function flush ( run , data = { } ) { const content = run ( ) ; if ( Object . keys ( requests ) . length ) { await Promise . allSettled ( Object . values ( requests ) ) ; return flush ( run , data ) ; } return { content , data : loadCache . dump ( ) } ; }