import { Fn , FunctionalComponent , GlobalStatsObject , OnCacheOperation , Options , StatsCache , StatsProfile , } from './types' ; export const statsCache = { anonymousProfileNameCounter : 1 , isCollectingStats : false , profiles : { } , } ; let hasWarningDisplayed = false ; export function clearStats ( profileName ? ) { if ( profileName ) { delete statsCache . profiles [ profileName ] ; } else { statsCache . profiles = { } ; } } private description param isCollectingStats export function collectStats ( isCollectingStats = true ) { statsCache . isCollectingStats = isCollectingStats ; } private description export function createOnCacheAddIncrementCalls ( options ) { const { profileName } = options ; return function ( ) { if ( profileName && ! statsCache . profiles [ profileName ] ) { statsCache . profiles [ profileName ] = { calls : 0 , hits : 0 , } ; } statsCache . profiles [ profileName ] . calls ++ ; } ; } private description export function createOnCacheHitIncrementCallsAndHits ( options ) { return function ( ) { const { profiles } = statsCache ; const { profileName } = options ; if ( ! profiles [ profileName ] ) { profiles [ profileName ] = { calls : 0 , hits : 0 , } ; } profiles [ profileName ] . calls ++ ; profiles [ profileName ] . hits ++ ; } ; } private description param fn returns export function getDefaultProfileName ( fn ) { const stack = new Error ( ) . stack ; const fnName = ( fn as FunctionalComponent < Record < string , unknown > > ) . displayName || fn . name || `Anonymous ${ statsCache . anonymousProfileNameCounter ++ }` ; if ( ! stack ) { return fnName ; } const lines = stack . split ( '\n' ) . slice ( 3 ) ; let line ; let profileNameLocation ; for ( let index = 0 ; index < lines . length ; index ++ ) { line = lines [ index ] ; if ( line . indexOf ( '/moize/' ) === - 1 && line . indexOf ( ' (native)' ) === - 1 && line . indexOf ( ' Function.' ) === - 1 ) { profileNameLocation = line . replace ( /\n/g , '\\n' ) . trim ( ) ; break ; } } return profileNameLocation ? `${ fnName } ${ profileNameLocation }` : fnName ; } private description param calls param hits returns export function getUsagePercentage ( calls , hits ) { return calls ? `${ ( ( hits / calls ) * 100 ) . toFixed ( 4 ) }%` : '0.0000%' ; } private description param profileName returns export function getStats ( profileName ? ) { if ( ! statsCache . isCollectingStats && ! hasWarningDisplayed ) { console . warn ( 'Stats are not currently being collected, please run "collectStats" to enable them.' ) ; hasWarningDisplayed = true ; } const { profiles } = statsCache ; if ( profileName ) { if ( ! profiles [ profileName ] ) { return { calls : 0 , hits : 0 , usage : '0.0000%' , } ; } const { [ profileName ] : profile } = profiles ; return { ... profile , usage : getUsagePercentage ( profile . calls , profile . hits ) , } ; } const completeStats = Object . keys ( statsCache . profiles ) . reduce ( ( completeProfiles , profileName ) => { completeProfiles . calls += profiles [ profileName ] . calls ; completeProfiles . hits += profiles [ profileName ] . hits ; return completeProfiles ; } , { calls : 0 , hits : 0 , } ) ; return { ... completeStats , profiles : Object . keys ( profiles ) . reduce ( ( computedProfiles , profileName ) => { computedProfiles [ profileName ] = getStats ( profileName ) ; return computedProfiles ; } , { } as Record < string , StatsProfile > ) , usage : getUsagePercentage ( completeStats . calls , completeStats . hits ) , } ; } private function description param { Options } options returns { Object } export function getStatsOptions ( options ) { return statsCache . isCollectingStats ? { onCacheAdd : createOnCacheAddIncrementCalls ( options ) , onCacheHit : createOnCacheHitIncrementCallsAndHits ( options ) , } : { } ; }