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-20 01:49:58 +00:00
import { UNCONFIGURABLE_PROPERTIES } from "./misc" ;
2024-05-11 04:11:57 +00:00
import { AnyObject } from "./types" ;
export type ProxyLazy < T = AnyObject > = T & {
2024-05-28 20:41:34 +00:00
[ SYM_LAZY_GET ] : ( ) = > T ;
[ SYM_LAZY_CACHED ] : T | undefined ;
2024-05-11 04:11:57 +00:00
} ;
2024-05-28 20:41:34 +00:00
export const SYM_LAZY_GET = Symbol . for ( "vencord.lazy.get" ) ;
export const SYM_LAZY_CACHED = Symbol . for ( "vencord.lazy.cached" ) ;
2024-05-11 04:11:57 +00:00
2024-05-26 02:51:59 +00:00
export type LazyFunction < T > = ( ( ) = > T ) & {
$ $vencordLazyFailed : ( ) = > boolean ;
} ;
export function makeLazy < T > ( factory : ( ) = > T , attempts = 5 , { isIndirect = false } : { isIndirect? : boolean ; } = { } ) : LazyFunction < 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 ) {
2024-05-16 02:06:21 +00:00
tries ++ ;
2023-09-23 01:25:19 +00:00
cache = factory ( ) ;
2024-05-16 02:06:21 +00:00
if ( ! cache && attempts === tries && ! isIndirect ) {
console . error ( ` makeLazy 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
}
2024-05-03 02:18:12 +00:00
const handler : ProxyHandler < any > = {
. . . Object . fromEntries ( Object . getOwnPropertyNames ( Reflect ) . map ( propName = >
2024-05-28 20:41:34 +00:00
[ propName , ( target : any , . . . args : any [ ] ) = > Reflect [ propName ] ( target [ SYM_LAZY_GET ] ( ) , . . . args ) ]
2024-05-03 02:18:12 +00:00
) ) ,
2024-05-24 01:52:32 +00:00
set : ( target , p , newValue ) = > {
2024-05-28 20:41:34 +00:00
const lazyTarget = target [ SYM_LAZY_GET ] ( ) ;
2024-05-24 01:52:32 +00:00
return Reflect . set ( lazyTarget , p , newValue , lazyTarget ) ;
} ,
2024-05-03 02:18:12 +00:00
ownKeys : target = > {
2024-05-28 20:41:34 +00:00
const keys = Reflect . ownKeys ( target [ SYM_LAZY_GET ] ( ) ) ;
2024-05-20 02:01:08 +00:00
for ( const key of UNCONFIGURABLE_PROPERTIES ) {
2024-05-03 02:18:12 +00:00
if ( ! keys . includes ( key ) ) keys . push ( key ) ;
}
return keys ;
} ,
getOwnPropertyDescriptor : ( target , p ) = > {
2024-05-20 02:01:08 +00:00
if ( typeof p === "string" && UNCONFIGURABLE_PROPERTIES . includes ( p ) )
2024-05-03 02:18:12 +00:00
return Reflect . getOwnPropertyDescriptor ( target , p ) ;
2022-11-06 17:00:59 +00:00
2024-05-28 20:41:34 +00:00
const descriptor = Reflect . getOwnPropertyDescriptor ( target [ SYM_LAZY_GET ] ( ) , p ) ;
2024-05-03 02:18:12 +00:00
if ( descriptor ) Object . defineProperty ( target , p , descriptor ) ;
return descriptor ;
2022-11-06 17:00:59 +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 .
2024-05-26 23:41:28 +00:00
* On first property access , the factory is evaluated .
2024-05-30 02:42:14 +00:00
*
2024-05-03 02:18:12 +00:00
* @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-11 04:11:57 +00:00
export function proxyLazy < T = AnyObject > ( factory : ( ) = > T , attempts = 5 , isChild = false ) : ProxyLazy < T > {
2024-05-16 02:07:35 +00:00
const get = makeLazy ( factory , attempts , { isIndirect : true } ) ;
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-24 02:14:00 +00:00
// Define the function in an object to preserve the name after minification
2024-05-24 02:20:46 +00:00
const proxyDummy = ( { ProxyDummy() { } } ) . ProxyDummy ;
2024-05-24 02:14:00 +00:00
Object . assign ( proxyDummy , {
2024-05-28 20:31:58 +00:00
[ SYM_LAZY_GET ] ( ) {
2024-05-28 20:41:34 +00:00
if ( ! proxyDummy [ SYM_LAZY_CACHED ] ) {
2024-05-04 23:56:32 +00:00
if ( ! get . $ $vencordLazyFailed ( ) ) {
2024-05-28 20:41:34 +00:00
proxyDummy [ SYM_LAZY_CACHED ] = get ( ) ;
2024-05-04 23:56:32 +00:00
}
2024-05-03 02:18:12 +00:00
2024-05-28 20:41:34 +00:00
if ( ! proxyDummy [ SYM_LAZY_CACHED ] ) {
2024-05-04 23:56:32 +00:00
throw new Error ( ` proxyLazy factory failed: \ n \ n ${ factory } ` ) ;
2024-05-24 01:20:24 +00:00
} else {
2024-05-28 20:41:34 +00:00
if ( typeof proxyDummy [ SYM_LAZY_CACHED ] === "function" ) {
proxy . toString = proxyDummy [ SYM_LAZY_CACHED ] . toString . bind ( proxyDummy [ SYM_LAZY_CACHED ] ) ;
2024-05-24 01:20:24 +00:00
}
2024-05-03 02:18:12 +00:00
}
2023-07-05 22:53:43 +00:00
}
2024-05-03 02:18:12 +00:00
2024-05-28 20:31:58 +00:00
return proxyDummy [ SYM_LAZY_CACHED ] ;
2024-05-03 02:18:12 +00:00
} ,
2024-05-28 20:41:34 +00:00
[ SYM_LAZY_CACHED ] : void 0 as T | undefined
2023-02-09 20:21:14 +00:00
} ) ;
2022-11-06 17:00:59 +00:00
2024-05-24 01:20:24 +00:00
const proxy = new Proxy ( proxyDummy , {
2023-11-22 05:48:59 +00:00
. . . handler ,
get ( target , p , receiver ) {
2024-05-28 20:41:34 +00:00
if ( p === SYM_LAZY_GET || p === SYM_LAZY_CACHED ) {
2024-05-28 20:31:58 +00:00
return Reflect . get ( target , p , receiver ) ;
2024-05-28 20:41:34 +00:00
}
2024-05-07 04:13:46 +00:00
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
2024-05-08 03:25:34 +00:00
// `const { meow } = proxyLazy(() => ({ meow: [] }));`
2024-05-03 02:18:12 +00:00
if ( ! isChild && isSameTick ) {
2023-11-22 05:48:59 +00:00
return proxyLazy (
2024-05-26 02:51:59 +00:00
( ) = > {
2024-05-28 20:41:34 +00:00
const lazyTarget = target [ SYM_LAZY_GET ] ( ) ;
2024-05-26 02:51:59 +00:00
return Reflect . get ( lazyTarget , p , lazyTarget ) ;
} ,
2023-11-22 05:48:59 +00:00
attempts ,
true
) ;
2024-05-03 02:18:12 +00:00
}
2024-05-28 20:31:58 +00:00
const lazyTarget = target [ SYM_LAZY_GET ] ( ) ;
2024-04-08 04:33:35 +00:00
if ( typeof lazyTarget === "object" || typeof lazyTarget === "function" ) {
2024-05-24 01:51:23 +00:00
return Reflect . get ( lazyTarget , p , lazyTarget ) ;
2024-04-08 04:33:35 +00:00
}
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-11 04:11:57 +00:00
} ) ;
2024-05-24 01:20:24 +00:00
return proxy ;
2022-10-14 19:34:35 +00:00
}