2022-10-21 23:17:06 +00:00
/ *
2024-05-03 02:18:12 +00:00
* Vencord , a Discord client mod
* Copyright ( c ) 2024 Vendicated and contributors
* SPDX - License - Identifier : GPL - 3.0 - or - later
* /
2022-10-21 23:17:06 +00:00
2024-05-07 04:13:46 +00:00
export function makeLazy < T > ( factory : ( ) = > T , attempts = 5 , { isIndirect = false } : { isIndirect? : boolean ; } = { } ) : ( ) = > T {
2023-09-23 01:25:19 +00:00
let tries = 0 ;
2023-05-05 23:36:00 +00:00
let cache : T ;
2024-05-04 23:56:32 +00:00
const getter = ( ) = > {
2024-05-05 05:52:41 +00:00
if ( ! cache && attempts > tries ) {
2023-09-23 01:25:19 +00:00
cache = factory ( ) ;
2024-05-07 04:13:46 +00:00
if ( ! cache && attempts === ++ tries && ! isIndirect ) {
2024-05-04 23:56:32 +00:00
console . error ( ` Lazy factory failed: \ n \ n ${ factory } ` ) ;
2024-05-03 02:18:12 +00:00
}
2023-09-23 01:25:19 +00:00
}
2024-05-07 04:13:46 +00:00
2023-09-23 01:25:19 +00:00
return cache ;
} ;
2024-05-04 23:56:32 +00:00
2024-05-05 05:52:41 +00:00
getter . $ $vencordLazyFailed = ( ) = > tries === attempts ;
2024-05-04 23:56:32 +00:00
return getter ;
2023-05-05 23:36:00 +00:00
}
2022-11-06 17:00:59 +00:00
// Proxies demand that these properties be unmodified, so proxyLazy
// will always return the function default for them.
const unconfigurable = [ "arguments" , "caller" , "prototype" ] ;
2022-10-14 19:34:35 +00:00
2024-05-03 02:18:12 +00:00
const handler : ProxyHandler < any > = {
. . . Object . fromEntries ( Object . getOwnPropertyNames ( Reflect ) . map ( propName = >
[ propName , ( target : any , . . . args : any [ ] ) = > Reflect [ propName ] ( target [ proxyLazyGet ] ( ) , . . . args ) ]
) ) ,
ownKeys : target = > {
const keys = Reflect . ownKeys ( target [ proxyLazyGet ] ( ) ) ;
for ( const key of unconfigurable ) {
if ( ! keys . includes ( key ) ) keys . push ( key ) ;
}
return keys ;
} ,
getOwnPropertyDescriptor : ( target , p ) = > {
if ( typeof p === "string" && unconfigurable . includes ( p ) )
return Reflect . getOwnPropertyDescriptor ( target , p ) ;
2022-11-06 17:00:59 +00:00
2024-05-03 02:18:12 +00:00
const descriptor = Reflect . getOwnPropertyDescriptor ( target [ proxyLazyGet ] ( ) , p ) ;
if ( descriptor ) Object . defineProperty ( target , p , descriptor ) ;
return descriptor ;
2022-11-06 17:00:59 +00:00
}
} ;
2024-05-07 04:13:46 +00:00
export const proxyLazyGet = Symbol . for ( "vencord.lazy.get" ) ;
export const proxyLazyCache = Symbol . for ( "vencord.lazy.cached" ) ;
2022-10-14 20:38:36 +00:00
2022-10-14 19:34:35 +00:00
/ * *
2024-05-03 02:18:12 +00:00
* Wraps the result of factory in a Proxy you can consume as if it wasn ' t lazy .
* On first property access , the factory is evaluated
* @param factory Factory returning the result
* @param attempts How many times to try to evaluate the factory before giving up
* @returns Result of factory function
2022-10-14 19:34:35 +00:00
* /
2024-05-03 02:18:12 +00:00
export function proxyLazy < T = any > ( factory : ( ) = > T , attempts = 5 , isChild = false ) : T {
2024-05-07 04:13:46 +00:00
const get = makeLazy ( factory , attempts , { isIndirect : true } ) as any ;
2024-05-03 02:18:12 +00:00
2023-11-22 05:48:59 +00:00
let isSameTick = true ;
2024-05-03 02:18:12 +00:00
if ( ! isChild ) setTimeout ( ( ) = > isSameTick = false , 0 ) ;
2023-11-22 05:48:59 +00:00
2024-05-07 05:57:17 +00:00
const proxyDummy = Object . assign ( function ProxyDummy() { } , {
2024-05-03 02:18:12 +00:00
[ proxyLazyGet ] ( ) {
2024-05-04 23:56:32 +00:00
if ( ! proxyDummy [ proxyLazyCache ] ) {
if ( ! get . $ $vencordLazyFailed ( ) ) {
proxyDummy [ proxyLazyCache ] = get ( ) ;
}
2024-05-03 02:18:12 +00:00
if ( ! proxyDummy [ proxyLazyCache ] ) {
2024-05-04 23:56:32 +00:00
throw new Error ( ` proxyLazy factory failed: \ n \ n ${ factory } ` ) ;
2024-05-03 02:18:12 +00:00
}
2023-07-05 22:53:43 +00:00
}
2024-05-03 02:18:12 +00:00
return proxyDummy [ proxyLazyCache ] ;
} ,
[ proxyLazyCache ] : void 0 as T | undefined
2023-02-09 20:21:14 +00:00
} ) ;
2022-11-06 17:00:59 +00:00
2023-11-22 05:48:59 +00:00
return new Proxy ( proxyDummy , {
. . . handler ,
get ( target , p , receiver ) {
2024-05-07 04:13:46 +00:00
if ( p === proxyLazyGet ) return target [ proxyLazyGet ] ;
if ( p === proxyLazyCache ) return target [ proxyLazyCache ] ;
2024-05-03 02:18:12 +00:00
// If we're still in the same tick, it means the lazy was immediately used.
2023-11-22 05:48:59 +00:00
// thus, we lazy proxy the get access to make things like destructuring work as expected
// meow here will also be a lazy
// `const { meow } = findByPropsLazy("meow");`
2024-05-03 02:18:12 +00:00
if ( ! isChild && isSameTick ) {
2023-11-22 05:48:59 +00:00
return proxyLazy (
2024-05-03 02:18:12 +00:00
( ) = > Reflect . get ( target [ proxyLazyGet ] ( ) , p , receiver ) ,
2023-11-22 05:48:59 +00:00
attempts ,
true
) ;
2024-05-03 02:18:12 +00:00
}
const lazyTarget = target [ proxyLazyGet ] ( ) ;
2024-04-08 04:33:35 +00:00
if ( typeof lazyTarget === "object" || typeof lazyTarget === "function" ) {
return Reflect . get ( lazyTarget , p , receiver ) ;
}
2024-05-03 02:18:12 +00:00
2024-05-04 20:49:20 +00:00
throw new Error ( "proxyLazy called on a primitive value. This can happen if you try to destructure a primitive at the same tick as the proxy was created." ) ;
2023-11-22 05:48:59 +00:00
}
2024-05-03 02:18:12 +00:00
} ) as T ;
2022-10-14 19:34:35 +00:00
}