2024-05-03 02:18:12 +00:00
/ *
2024-05-08 00:44:26 +00:00
* Vencord , a Discord client mod
* Copyright ( c ) 2024 Vendicated and contributors
* SPDX - License - Identifier : GPL - 3.0 - or - later
* /
2024-05-03 02:18:12 +00:00
2024-05-16 02:02:13 +00:00
import { makeLazy , proxyLazy } from "@utils/lazy" ;
2024-05-03 02:18:12 +00:00
import { LazyComponent } from "@utils/lazyReact" ;
import { Logger } from "@utils/Logger" ;
import { canonicalizeMatch } from "@utils/patches" ;
2024-05-11 04:11:57 +00:00
import { ProxyInner , proxyInner , proxyInnerValue } from "@utils/proxyInner" ;
import { AnyObject } from "@utils/types" ;
2024-05-03 02:18:12 +00:00
import { traceFunction } from "../debug/Tracer" ;
2024-05-11 09:05:20 +00:00
import { GenericStore } from "./common" ;
2024-05-23 09:51:07 +00:00
import { ModuleExports , ModuleFactory , WebpackRequire } from "./wreq" ;
2024-05-03 02:18:12 +00:00
const logger = new Logger ( "Webpack" ) ;
export let _resolveReady : ( ) = > void ;
/ * *
* Fired once a gateway connection to Discord has been established .
* This indicates that the core webpack modules have been initialised
* /
export const onceReady = new Promise < void > ( r = > _resolveReady = r ) ;
2024-05-23 09:51:07 +00:00
export let wreq : WebpackRequire ;
export let cache : WebpackRequire [ "c" ] ;
2024-05-03 02:18:12 +00:00
2024-05-23 09:51:07 +00:00
export type FilterFn = ( module : ModuleExports ) = > boolean ;
2024-05-03 02:18:12 +00:00
export const filters = {
byProps : ( . . . props : string [ ] ) : FilterFn = > {
const filter = props . length === 1
? m = > m ? . [ props [ 0 ] ] !== void 0
: m = > props . every ( p = > m ? . [ p ] !== void 0 ) ;
// @ts-ignore
filter . $ $vencordProps = [ "byProps" , . . . props ] ;
return filter ;
} ,
byCode : ( . . . code : string [ ] ) : FilterFn = > {
const filter = m = > {
if ( typeof m !== "function" ) return false ;
const s = Function . prototype . toString . call ( m ) ;
for ( const c of code ) {
if ( ! s . includes ( c ) ) return false ;
}
return true ;
} ;
filter . $ $vencordProps = [ "byCode" , . . . code ] ;
return filter ;
} ,
byStoreName : ( name : string ) : FilterFn = > {
const filter = m = > m ? . constructor ? . displayName === name ;
filter . $ $vencordProps = [ "byStoreName" , name ] ;
return filter ;
} ,
componentByCode : ( . . . code : string [ ] ) : FilterFn = > {
const filter = filters . byCode ( . . . code ) ;
const wrapper = m = > {
if ( filter ( m ) ) return true ;
if ( ! m ? . $ $typeof ) return false ;
if ( m ? . type && m . type . render ) return filter ( m . type . render ) ; // memo + forwardRef
if ( m ? . type ) return filter ( m . type ) ; // memos
if ( m ? . render ) return filter ( m . render ) ; // forwardRefs
return false ;
} ;
wrapper . $ $vencordProps = [ "componentByCode" , . . . code ] ;
return wrapper ;
}
} ;
2024-05-23 09:58:51 +00:00
export type ModCallbackFn = ( module : ModuleExports ) = > void ;
export type ModCallbackFnWithId = ( module : ModuleExports , id : PropertyKey ) = > void ;
2024-05-03 02:18:12 +00:00
export const waitForSubscriptions = new Map < FilterFn , ModCallbackFn > ( ) ;
export const moduleListeners = new Set < ModCallbackFnWithId > ( ) ;
2024-05-23 09:51:07 +00:00
export const factoryListeners = new Set < ( factory : ModuleFactory ) = > void > ( ) ;
2024-05-03 02:18:12 +00:00
2024-05-23 09:51:07 +00:00
export function _initWebpack ( webpackRequire : WebpackRequire ) {
2024-05-03 02:18:12 +00:00
wreq = webpackRequire ;
cache = webpackRequire . c ;
2024-05-24 10:41:52 +00:00
2024-05-24 11:02:04 +00:00
Object . defineProperty ( webpackRequire . c , Symbol . toStringTag , {
value : "ModuleCache" ,
configurable : true ,
writable : true
} ) ;
2024-05-03 02:18:12 +00:00
}
let devToolsOpen = false ;
if ( IS_DEV && IS_DISCORD_DESKTOP ) {
// At this point in time, DiscordNative has not been exposed yet, so setImmediate is needed
setTimeout ( ( ) = > {
DiscordNative /* just to make sure */ ? . window . setDevtoolsCallbacks ( ( ) = > devToolsOpen = true , ( ) = > devToolsOpen = false ) ;
} , 0 ) ;
}
export const webpackSearchHistory = [ ] as Array < [ "waitFor" | "find" | "findComponent" | "findExportedComponent" | "findComponentByCode" | "findByProps" | "findByCode" | "findStore" | "extractAndLoadChunks" | "webpackDependantLazy" | "webpackDependantLazyComponent" , any [ ] ] > ;
function printFilter ( filter : FilterFn ) {
if ( "$$vencordProps" in filter ) {
const props = filter . $ $vencordProps as string [ ] ;
return ` ${ props [ 0 ] } ( ${ props . slice ( 1 ) . map ( arg = > ` " ${ arg } " ` ) . join ( ", " ) } ) ` ;
}
return filter . toString ( ) ;
}
/ * *
* Wait for the first module that matches the provided filter to be required ,
* then call the callback with the module as the first argument .
*
* If the module is already required , the callback will be called immediately .
* @param filter A function that takes a module and returns a boolean
* @param callback A function that takes the found module as its first argument
* /
export function waitFor ( filter : FilterFn , callback : ModCallbackFn , { isIndirect = false } : { isIndirect? : boolean ; } = { } ) {
if ( typeof filter !== "function" )
throw new Error ( "Invalid filter. Expected a function got " + typeof filter ) ;
if ( typeof callback !== "function" )
throw new Error ( "Invalid callback. Expected a function got " + typeof callback ) ;
2024-05-07 04:13:46 +00:00
if ( IS_DEV && ! isIndirect ) {
const originalCallback = callback ;
let callbackCalled = false ;
callback = function ( ) {
callbackCalled = true ;
// @ts-ignore
originalCallback ( . . . arguments ) ;
} ;
// @ts-ignore
callback . $ $vencordCallbackCalled = ( ) = > callbackCalled ;
webpackSearchHistory . push ( [ "waitFor" , [ callback , filter ] ] ) ;
}
2024-05-03 02:18:12 +00:00
if ( cache != null ) {
const existing = cacheFind ( filter ) ;
if ( existing ) return callback ( existing ) ;
}
waitForSubscriptions . set ( filter , callback ) ;
}
/ * *
* Find the first module that matches the filter .
*
* The way this works internally is :
* Wait for the first module that matches the provided filter to be required ,
* then call the callback with the module as the first argument .
*
* If the module is already required , the callback will be called immediately .
*
* The callback must return a value that will be used as the proxy inner value .
*
2024-05-07 09:23:36 +00:00
* If no callback is specified , the default callback will assign the proxy inner value to all the module .
2024-05-03 02:18:12 +00:00
* @param filter A function that takes a module and returns a boolean
2024-05-07 09:23:36 +00:00
* @param callback A function that takes the found module as its first argument and returns something to use as the proxy inner value . Useful if you want to use a value from the module , instead of all of it . Defaults to the module itself
2024-05-03 02:18:12 +00:00
* @returns A proxy that has the callback return value as its true value , or the callback return value if the callback was called when the function was called
* /
2024-05-23 09:58:51 +00:00
export function find < T = AnyObject > ( filter : FilterFn , callback : ( module : ModuleExports ) = > any = m = > m , { isIndirect = false } : { isIndirect? : boolean ; } = { } ) {
2024-05-03 02:18:12 +00:00
if ( typeof filter !== "function" )
throw new Error ( "Invalid filter. Expected a function got " + typeof filter ) ;
if ( typeof callback !== "function" )
throw new Error ( "Invalid callback. Expected a function got " + typeof callback ) ;
2024-05-04 20:49:20 +00:00
const [ proxy , setInnerValue ] = proxyInner < T > ( ` Webpack find matched no module. Filter: ${ printFilter ( filter ) } ` , "Webpack find with proxy called on a primitive value. This can happen if you try to destructure a primitive in the top level definition of the find." ) ;
2024-05-24 03:14:07 +00:00
waitFor ( filter , module = > setInnerValue ( callback ( module ) ) , { isIndirect : true } ) ;
2024-05-03 02:18:12 +00:00
2024-05-07 04:13:46 +00:00
if ( IS_DEV && ! isIndirect ) {
webpackSearchHistory . push ( [ "find" , [ proxy , filter ] ] ) ;
}
2024-05-11 04:11:57 +00:00
if ( proxy [ proxyInnerValue ] != null ) return proxy [ proxyInnerValue ] as ProxyInner < T > ;
2024-05-03 02:18:12 +00:00
return proxy ;
}
/ * *
* Find the first component that matches the filter .
2024-05-08 09:44:18 +00:00
*
* IMPORTANT : You cannot access properties set on the found component using this method . You should instead try to obtain the property using a non component find instead .
*
* Example of how you cannot access the properties set on the component :
* ` ` `
* const Component = findComponent ( . . . ) ;
* console . log ( Component . Types ) ; // This will not work
* ` ` ` `
2024-05-03 02:18:12 +00:00
* @param filter A function that takes a module and returns a boolean
* @param parse A function that takes the found component as its first argument and returns a component . Useful if you want to wrap the found component in something . Defaults to the original component
* @returns The component if found , or a noop component
* /
2024-05-23 09:58:51 +00:00
export function findComponent < T extends object = any > ( filter : FilterFn , parse : ( component : ModuleExports ) = > React . ComponentType < T > = m = > m , { isIndirect = false } : { isIndirect? : boolean ; } = { } ) {
2024-05-03 02:18:12 +00:00
if ( typeof filter !== "function" )
throw new Error ( "Invalid filter. Expected a function got " + typeof filter ) ;
if ( typeof parse !== "function" )
throw new Error ( "Invalid component parse. Expected a function got " + typeof parse ) ;
2024-05-07 04:13:46 +00:00
let InnerComponent = null as React . ComponentType < T > | null ;
2024-05-07 02:22:36 +00:00
let findFailedLogged = false ;
const WrapperComponent = ( props : T ) = > {
if ( InnerComponent === null && ! findFailedLogged ) {
findFailedLogged = true ;
2024-05-03 02:18:12 +00:00
logger . error ( ` Webpack find matched no module. Filter: ${ printFilter ( filter ) } ` ) ;
}
2024-05-07 02:22:36 +00:00
return InnerComponent && < InnerComponent { ...props } / > ;
2024-05-03 02:18:12 +00:00
} ;
waitFor ( filter , ( v : any ) = > {
const parsedComponent = parse ( v ) ;
InnerComponent = parsedComponent ;
Object . assign ( InnerComponent , parsedComponent ) ;
} , { isIndirect : true } ) ;
2024-05-07 04:13:46 +00:00
if ( IS_DEV ) {
WrapperComponent . $ $vencordInner = ( ) = > InnerComponent ;
if ( ! isIndirect ) {
webpackSearchHistory . push ( [ "findComponent" , [ WrapperComponent , filter ] ] ) ;
}
}
2024-05-07 02:22:36 +00:00
if ( InnerComponent !== null ) return InnerComponent ;
2024-05-03 02:18:12 +00:00
2024-05-11 04:11:57 +00:00
return WrapperComponent as React . ComponentType < T > ;
2024-05-03 02:18:12 +00:00
}
/ * *
* Find the first component that is exported by the first prop name .
*
2024-05-08 09:44:18 +00:00
* IMPORTANT : You cannot access properties set on the found component using this method . You should instead try to obtain the property using a non component find instead .
*
* Example of how you cannot access the properties set on the component :
* ` ` `
* const Component = findExportedComponent ( . . . ) ;
* console . log ( Component . Types ) ; // This will not work
* ` ` ` `
2024-05-03 02:18:12 +00:00
* @example findExportedComponent ( "FriendRow" )
* @example findExportedComponent ( "FriendRow" , "Friend" , FriendRow = > React . memo ( FriendRow ) )
*
* @param props A list of prop names to search the exports for
* @param parse A function that takes the found component as its first argument and returns a component . Useful if you want to wrap the found component in something . Defaults to the original component
* @returns The component if found , or a noop component
* /
2024-05-23 09:58:51 +00:00
export function findExportedComponent < T extends object = any > ( . . . props : string [ ] | [ . . . string [ ] , ( component : ModuleExports ) = > React . ComponentType < T > ] ) {
const parse = ( typeof props . at ( - 1 ) === "function" ? props . pop ( ) : m = > m ) as ( component : ModuleExports ) = > React . ComponentType < T > ;
2024-05-03 02:18:12 +00:00
const newProps = props as string [ ] ;
const filter = filters . byProps ( . . . newProps ) ;
2024-05-07 04:13:46 +00:00
let InnerComponent = null as React . ComponentType < T > | null ;
2024-05-07 02:22:36 +00:00
let findFailedLogged = false ;
const WrapperComponent = ( props : T ) = > {
if ( InnerComponent === null && ! findFailedLogged ) {
findFailedLogged = true ;
2024-05-03 02:18:12 +00:00
logger . error ( ` Webpack find matched no module. Filter: ${ printFilter ( filter ) } ` ) ;
}
2024-05-07 02:22:36 +00:00
return InnerComponent && < InnerComponent { ...props } / > ;
2024-05-03 02:18:12 +00:00
} ;
waitFor ( filter , ( v : any ) = > {
const parsedComponent = parse ( v [ newProps [ 0 ] ] ) ;
InnerComponent = parsedComponent ;
Object . assign ( InnerComponent , parsedComponent ) ;
} , { isIndirect : true } ) ;
2024-05-07 04:13:46 +00:00
if ( IS_DEV ) {
WrapperComponent . $ $vencordInner = ( ) = > InnerComponent ;
webpackSearchHistory . push ( [ "findExportedComponent" , [ WrapperComponent , . . . props ] ] ) ;
}
2024-05-07 02:22:36 +00:00
if ( InnerComponent !== null ) return InnerComponent ;
2024-05-03 02:18:12 +00:00
return WrapperComponent as React . ComponentType < T > ;
}
/ * *
2024-05-07 09:28:43 +00:00
* Find the first component in a default export that includes all the given code .
2024-05-03 02:18:12 +00:00
*
2024-05-08 09:44:18 +00:00
* IMPORTANT : You cannot access properties set on the found component using this method . You should instead try to obtain the property using a non component find instead .
*
* Example of how you cannot access the properties set on the component :
* ` ` `
* const Component = findComponentByCode ( . . . ) ;
* console . log ( Component . Types ) ; // This will not work
* ` ` ` `
2024-05-03 02:18:12 +00:00
* @example findComponentByCode ( ".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR" )
* @example findComponentByCode ( ".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR" , ".BACKGROUND_PRIMARY)" , ColorPicker = > React . memo ( ColorPicker ) )
*
* @param code A list of code to search each export for
* @param parse A function that takes the found component as its first argument and returns a component . Useful if you want to wrap the found component in something . Defaults to the original component
* @returns The component if found , or a noop component
* /
2024-05-23 09:58:51 +00:00
export function findComponentByCode < T extends object = any > ( . . . code : string [ ] | [ . . . string [ ] , ( component : ModuleExports ) = > React . ComponentType < T > ] ) {
const parse = ( typeof code . at ( - 1 ) === "function" ? code . pop ( ) : m = > m ) as ( component : ModuleExports ) = > React . ComponentType < T > ;
2024-05-03 02:18:12 +00:00
const newCode = code as string [ ] ;
2024-05-07 04:13:46 +00:00
const ComponentResult = findComponent < T > ( filters . componentByCode ( . . . newCode ) , parse , { isIndirect : true } ) ;
if ( IS_DEV ) {
webpackSearchHistory . push ( [ "findComponentByCode" , [ ComponentResult , . . . code ] ] ) ;
}
2024-05-03 02:18:12 +00:00
2024-05-07 04:13:46 +00:00
return ComponentResult ;
2024-05-03 02:18:12 +00:00
}
/ * *
2024-05-07 09:23:36 +00:00
* Find the first module or default export that includes all the given props .
2024-05-03 02:18:12 +00:00
*
* @param props A list of props to search the exports for
* /
2024-05-11 04:11:57 +00:00
export function findByProps < T = AnyObject > ( . . . props : string [ ] ) {
2024-05-07 04:13:46 +00:00
const result = find < T > ( filters . byProps ( . . . props ) , m = > m , { isIndirect : true } ) ;
2024-05-03 02:18:12 +00:00
2024-05-07 04:13:46 +00:00
if ( IS_DEV ) {
webpackSearchHistory . push ( [ "findByProps" , [ result , . . . props ] ] ) ;
}
return result ;
2024-05-03 02:18:12 +00:00
}
/ * *
2024-05-07 09:28:43 +00:00
* Find the first default export that includes all the given code .
2024-05-03 02:18:12 +00:00
*
* @param code A list of code to search each export for
* /
2024-05-11 04:11:57 +00:00
export function findByCode < T = AnyObject > ( . . . code : string [ ] ) {
2024-05-07 04:13:46 +00:00
const result = find < T > ( filters . byCode ( . . . code ) , m = > m , { isIndirect : true } ) ;
if ( IS_DEV ) {
webpackSearchHistory . push ( [ "findByCode" , [ result , . . . code ] ] ) ;
}
2024-05-03 02:18:12 +00:00
2024-05-07 04:13:46 +00:00
return result ;
2024-05-03 02:18:12 +00:00
}
/ * *
2024-05-07 09:23:36 +00:00
* Find a store by its name .
2024-05-03 02:18:12 +00:00
*
* @param name The store name
* /
2024-05-11 09:05:20 +00:00
export function findStore < T = GenericStore > ( name : string ) {
2024-05-07 04:13:46 +00:00
const result = find < T > ( filters . byStoreName ( name ) , m = > m , { isIndirect : true } ) ;
if ( IS_DEV ) {
webpackSearchHistory . push ( [ "findStore" , [ result , name ] ] ) ;
}
2024-05-03 02:18:12 +00:00
2024-05-07 04:13:46 +00:00
return result ;
2024-05-03 02:18:12 +00:00
}
/ * *
* Find the first already required module that matches the filter .
* @param filter A function that takes a module and returns a boolean
* @returns The found module or null
* /
2024-05-08 03:25:34 +00:00
export const cacheFind = traceFunction ( "cacheFind" , function cacheFind ( filter : FilterFn ) {
2024-05-03 02:18:12 +00:00
if ( typeof filter !== "function" )
throw new Error ( "Invalid filter. Expected a function got " + typeof filter ) ;
for ( const key in cache ) {
const mod = cache [ key ] ;
if ( ! mod ? . exports || mod . exports === window ) continue ;
if ( filter ( mod . exports ) ) {
return mod . exports ;
}
if ( mod . exports . default && filter ( mod . exports . default ) ) {
return mod . exports . default ;
}
}
return null ;
} ) ;
export function cacheFindAll ( filter : FilterFn ) {
if ( typeof filter !== "function" )
throw new Error ( "Invalid filter. Expected a function got " + typeof filter ) ;
2024-05-23 09:51:07 +00:00
const ret : ModuleExports [ ] = [ ] ;
2024-05-03 02:18:12 +00:00
for ( const key in cache ) {
const mod = cache [ key ] ;
if ( ! mod ? . exports ) continue ;
if ( filter ( mod . exports ) ) {
ret . push ( mod . exports ) ;
}
if ( mod . exports . default && filter ( mod . exports . default ) ) {
ret . push ( mod . exports . default ) ;
}
}
return ret ;
}
/ * *
2024-05-07 09:23:36 +00:00
* Same as { @link cacheFind } but in bulk .
2024-05-03 02:18:12 +00:00
* @param filterFns Array of filters . Please note that this array will be modified in place , so if you still
* need it afterwards , pass a copy .
* @returns Array of results in the same order as the passed filters
* /
2024-05-08 03:25:34 +00:00
export const cacheFindBulk = traceFunction ( "cacheFindBulk" , function cacheFindBulk ( . . . filterFns : FilterFn [ ] ) {
2024-05-03 02:18:12 +00:00
if ( ! Array . isArray ( filterFns ) )
throw new Error ( "Invalid filters. Expected function[] got " + typeof filterFns ) ;
const { length } = filterFns ;
if ( length === 0 )
throw new Error ( "Expected at least two filters." ) ;
if ( length === 1 ) {
if ( IS_DEV ) {
throw new Error ( "bulk called with only one filter. Use find" ) ;
}
2024-05-11 04:11:57 +00:00
return [ cacheFind ( filterFns [ 0 ] ) ] ;
2024-05-03 02:18:12 +00:00
}
let found = 0 ;
2024-05-23 09:51:07 +00:00
const results : ModuleExports [ ] = Array ( length ) ;
2024-05-03 02:18:12 +00:00
outer :
for ( const key in cache ) {
const mod = cache [ key ] ;
if ( ! mod ? . exports ) continue ;
for ( let j = 0 ; j < length ; j ++ ) {
const filter = filterFns [ j ] ;
if ( filter ( mod . exports ) ) {
results [ j ] = mod . exports ;
filterFns . splice ( j -- , 1 ) ;
if ( ++ found === length ) break outer ;
break ;
}
if ( mod . exports . default && filter ( mod . exports . default ) ) {
results [ j ] = mod . exports . default ;
filterFns . splice ( j -- , 1 ) ;
if ( ++ found === length ) break outer ;
break ;
}
}
}
if ( found !== length ) {
const err = new Error ( ` Got ${ length } filters, but only found ${ found } modules! ` ) ;
if ( ! IS_DEV ) {
logger . warn ( err ) ;
return results ;
}
if ( ! devToolsOpen ) {
throw err ; // Strict behaviour in DevBuilds to fail early and make sure the issue is found
}
}
return results ;
} ) ;
/ * *
2024-05-07 09:23:36 +00:00
* Find the id of the first module factory that includes all the given code .
2024-05-03 02:18:12 +00:00
* @returns string or null
* /
export const findModuleId = traceFunction ( "findModuleId" , function findModuleId ( . . . code : string [ ] ) {
outer :
for ( const id in wreq . m ) {
2024-05-24 03:00:36 +00:00
const str = String ( wreq . m [ id ] ) ;
2024-05-03 02:18:12 +00:00
for ( const c of code ) {
if ( ! str . includes ( c ) ) continue outer ;
}
return id ;
}
const err = new Error ( "Didn't find module with code(s):\n" + code . join ( "\n" ) ) ;
if ( ! IS_DEV ) {
logger . warn ( err ) ;
return null ;
}
if ( ! devToolsOpen ) {
throw err ; // Strict behaviour in DevBuilds to fail early and make sure the issue is found
}
return null ;
} ) ;
/ * *
2024-05-07 09:23:36 +00:00
* Find the first module factory that includes all the given code .
2024-05-03 02:18:12 +00:00
* @returns The module factory or null
* /
export function findModuleFactory ( . . . code : string [ ] ) {
const id = findModuleId ( . . . code ) ;
if ( ! id ) return null ;
return wreq . m [ id ] ;
}
/ * *
* This is just a wrapper around { @link proxyLazy } to make our reporter test for your webpack finds .
*
* Wraps the result of factory in a Proxy you can consume as if it wasn ' t lazy .
2024-05-07 09:23:36 +00:00
* On first property access , the factory is evaluated .
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
* /
2024-05-11 04:11:57 +00:00
export function webpackDependantLazy < T = AnyObject > ( factory : ( ) = > T , attempts? : number ) {
2024-05-03 02:18:12 +00:00
if ( IS_DEV ) webpackSearchHistory . push ( [ "webpackDependantLazy" , [ factory ] ] ) ;
return proxyLazy < T > ( factory , attempts ) ;
}
/ * *
* This is just a wrapper around { @link LazyComponent } to make our reporter test for your webpack finds .
*
* A lazy component . The factory method is called on first render .
2024-05-08 09:44:18 +00:00
*
* IMPORTANT : You cannot access properties set on the lazy component using this method .
*
* Example of how you cannot access the properties set on the component :
* ` ` `
* const Component = webpackDependantLazyComponent ( . . . ) ;
* console . log ( Component . Types ) ; // This will not work
* ` ` ` `
2024-05-03 02:18:12 +00:00
* @param factory Function returning a Component
* @param attempts How many times to try to get the component before giving up
* @returns Result of factory function
* /
export function webpackDependantLazyComponent < T extends object = any > ( factory : ( ) = > any , attempts? : number ) {
if ( IS_DEV ) webpackSearchHistory . push ( [ "webpackDependantLazyComponent" , [ factory ] ] ) ;
return LazyComponent < T > ( factory , attempts ) ;
}
2024-05-08 00:33:05 +00:00
function deprecatedRedirect < T extends ( ...args : any [ ] ) = > any > ( oldMethod : string , newMethod : string , redirect : T ) : T {
return ( ( . . . args : Parameters < T > ) = > {
logger . warn ( ` Method ${ oldMethod } is deprecated. Use ${ newMethod } instead. For more information read https://github.com/Vendicated/Vencord/pull/2409#issue-2277161516 ` ) ;
return redirect ( . . . args ) ;
} ) as T ;
}
2024-05-03 08:35:53 +00:00
/ * *
* @deprecated Use { @link webpackDependantLazy } instead
2024-05-03 22:39:21 +00:00
*
2024-05-03 08:35:53 +00:00
* This is just a wrapper around { @link proxyLazy } to make our reporter test for your webpack finds .
*
* Wraps the result of factory in a Proxy you can consume as if it wasn ' t lazy .
2024-05-07 09:23:36 +00:00
* On first property access , the factory is evaluated .
2024-05-03 08:35:53 +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
* /
2024-05-08 00:33:05 +00:00
export const proxyLazyWebpack = deprecatedRedirect ( "proxyLazyWebpack" , "webpackDependantLazy" , webpackDependantLazy ) ;
2024-05-03 08:35:53 +00:00
/ * *
* @deprecated Use { @link webpackDependantLazyComponent } instead
2024-05-03 22:39:21 +00:00
*
2024-05-03 08:35:53 +00:00
* This is just a wrapper around { @link LazyComponent } to make our reporter test for your webpack finds .
*
* A lazy component . The factory method is called on first render .
2024-05-08 09:44:18 +00:00
*
* IMPORTANT : You cannot access properties set on the lazy component using this method .
*
* Example of how you cannot access the properties set on the component :
* ` ` `
* const Component = LazyComponentWebpack ( . . . ) ;
* console . log ( Component . Types ) ; // This will not work
* ` ` ` `
2024-05-03 08:35:53 +00:00
* @param factory Function returning a Component
* @param attempts How many times to try to get the component before giving up
* @returns Result of factory function
* /
2024-05-08 00:33:05 +00:00
export const LazyComponentWebpack = deprecatedRedirect ( "LazyComponentWebpack" , "webpackDependantLazyComponent" , webpackDependantLazyComponent ) ;
2024-05-03 08:35:53 +00:00
/ * *
* @deprecated Use { @link find } instead
2024-05-03 22:39:21 +00:00
*
2024-05-03 08:35:53 +00:00
* Find the first module that matches the filter , lazily
* /
2024-05-08 00:33:05 +00:00
export const findLazy = deprecatedRedirect ( "findLazy" , "find" , find ) ;
2024-05-03 08:35:53 +00:00
/ * *
* @deprecated Use { @link findByProps } instead
2024-05-03 22:39:21 +00:00
*
2024-05-03 08:35:53 +00:00
* Find the first module that has the specified properties , lazily
* /
2024-05-08 00:33:05 +00:00
export const findByPropsLazy = deprecatedRedirect ( "findByPropsLazy" , "findByProps" , findByProps ) ;
2024-05-03 08:35:53 +00:00
/ * *
* @deprecated Use { @link findByCode } instead
2024-05-03 22:39:21 +00:00
*
2024-05-03 08:35:53 +00:00
* Find the first function that includes all the given code , lazily
* /
2024-05-08 00:33:05 +00:00
export const findByCodeLazy = deprecatedRedirect ( "findByCodeLazy" , "findByCode" , findByCode ) ;
2024-05-03 08:35:53 +00:00
/ * *
* @deprecated Use { @link findStore } instead
2024-05-03 22:39:21 +00:00
*
2024-05-03 08:35:53 +00:00
* Find a store by its displayName , lazily
* /
2024-05-08 00:33:05 +00:00
export const findStoreLazy = deprecatedRedirect ( "findStoreLazy" , "findStore" , findStore ) ;
2024-05-03 08:35:53 +00:00
/ * *
* @deprecated Use { @link findComponent } instead
2024-05-03 22:39:21 +00:00
*
2024-05-03 08:35:53 +00:00
* Finds the first component that matches the filter , lazily .
* /
2024-05-08 00:33:05 +00:00
export const findComponentLazy = deprecatedRedirect ( "findComponentLazy" , "findComponent" , findComponent ) ;
2024-05-03 08:35:53 +00:00
/ * *
* @deprecated Use { @link findComponentByCode } instead
2024-05-03 22:39:21 +00:00
*
2024-05-03 08:35:53 +00:00
* Finds the first component that includes all the given code , lazily
* /
2024-05-08 00:33:05 +00:00
export const findComponentByCodeLazy = deprecatedRedirect ( "findComponentByCodeLazy" , "findComponentByCode" , findComponentByCode ) ;
2024-05-03 08:35:53 +00:00
/ * *
* @deprecated Use { @link findExportedComponent } instead
2024-05-03 22:39:21 +00:00
*
2024-05-03 08:35:53 +00:00
* Finds the first component that is exported by the first prop name , lazily
* /
2024-05-08 00:33:05 +00:00
export const findExportedComponentLazy = deprecatedRedirect ( "findExportedComponentLazy" , "findExportedComponent" , findExportedComponent ) ;
2024-05-03 08:35:53 +00:00
/ * *
* @deprecated Use { @link cacheFindAll } instead
* /
2024-05-08 00:33:05 +00:00
export const findAll = deprecatedRedirect ( "findAll" , "cacheFindAll" , cacheFindAll ) ;
2024-05-03 08:35:53 +00:00
/ * *
* @deprecated Use { @link cacheFindBulk } instead
2024-05-03 22:39:21 +00:00
*
2024-05-03 08:35:53 +00:00
* Same as { @link cacheFind } but in bulk
* @param filterFns Array of filters . Please note that this array will be modified in place , so if you still
* need it afterwards , pass a copy .
* @returns Array of results in the same order as the passed filters
* /
2024-05-08 00:33:05 +00:00
export const findBulk = deprecatedRedirect ( "findBulk" , "cacheFindBulk" , cacheFindBulk ) ;
2024-05-03 08:35:53 +00:00
2024-05-22 04:01:58 +00:00
export const DefaultExtractAndLoadChunksRegex = /(?:Promise\.all\(\[(\i\.\i\("[^)]+?"\)[^\]]+?)\]\)|(\i\.\i\("[^)]+?"\))|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/ ;
export const ChunkIdsRegex = /\("(.+?)"\)/g ;
2024-05-03 02:18:12 +00:00
/ * *
2024-05-07 09:23:36 +00:00
* Extract and load chunks using their entry point .
2024-05-03 02:18:12 +00:00
* @param code An array of all the code the module factory containing the lazy chunk loading must include
* @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second . Defaults to a matcher that captures the lazy chunk loading found in the module factory
* @returns A promise that resolves when the chunks were loaded
* /
export async function extractAndLoadChunks ( code : string [ ] , matcher : RegExp = DefaultExtractAndLoadChunksRegex ) {
const module = findModuleFactory ( . . . code ) ;
if ( ! module ) {
const err = new Error ( "extractAndLoadChunks: Couldn't find module factory" ) ;
logger . warn ( err , "Code:" , code , "Matcher:" , matcher ) ;
return ;
}
2024-05-24 03:00:36 +00:00
const match = String ( module ) . match ( canonicalizeMatch ( matcher ) ) ;
2024-05-03 02:18:12 +00:00
if ( ! match ) {
const err = new Error ( "extractAndLoadChunks: Couldn't find chunk loading in module factory code" ) ;
logger . warn ( err , "Code:" , code , "Matcher:" , matcher ) ;
// Strict behaviour in DevBuilds to fail early and make sure the issue is found
if ( IS_DEV && ! devToolsOpen )
throw err ;
return ;
}
2024-05-22 04:01:58 +00:00
const [ , rawChunkIdsArray , rawChunkIdsSingle , entryPointId ] = match ;
2024-05-15 02:53:41 +00:00
if ( Number . isNaN ( Number ( entryPointId ) ) ) {
2024-05-03 02:18:12 +00:00
const err = new Error ( "extractAndLoadChunks: Matcher didn't return a capturing group with the chunk ids array, or the entry point id returned as the second group wasn't a number" ) ;
logger . warn ( err , "Code:" , code , "Matcher:" , matcher ) ;
// Strict behaviour in DevBuilds to fail early and make sure the issue is found
if ( IS_DEV && ! devToolsOpen )
throw err ;
return ;
}
2024-05-22 04:01:58 +00:00
const rawChunkIds = rawChunkIdsArray ? ? rawChunkIdsSingle ;
2024-05-03 02:18:12 +00:00
if ( rawChunkIds ) {
2024-05-22 04:01:58 +00:00
const chunkIds = Array . from ( rawChunkIds . matchAll ( ChunkIdsRegex ) ) . map ( ( m : any ) = > m [ 1 ] ) ;
2024-05-03 02:18:12 +00:00
await Promise . all ( chunkIds . map ( id = > wreq . e ( id ) ) ) ;
}
wreq ( entryPointId ) ;
}
/ * *
* This is just a wrapper around { @link extractAndLoadChunks } to make our reporter test for your webpack finds .
*
2024-05-07 09:23:36 +00:00
* Extract and load chunks using their entry point .
2024-05-03 02:18:12 +00:00
* @param code An array of all the code the module factory containing the lazy chunk loading must include
* @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second . Defaults to a matcher that captures the lazy chunk loading found in the module factory
* @returns A function that returns a promise that resolves when the chunks were loaded , on first call
* /
export function extractAndLoadChunksLazy ( code : string [ ] , matcher = DefaultExtractAndLoadChunksRegex ) {
if ( IS_DEV ) webpackSearchHistory . push ( [ "extractAndLoadChunks" , [ code , matcher ] ] ) ;
2024-05-16 02:02:13 +00:00
return makeLazy ( ( ) = > extractAndLoadChunks ( code , matcher ) ) ;
2024-05-03 02:18:12 +00:00
}
/ * *
* Search modules by keyword . This searches the factory methods ,
* meaning you can search all sorts of things , displayName , methodName , strings somewhere in the code , etc
* @param filters One or more strings or regexes
* @returns Mapping of found modules
* /
export function search ( . . . filters : Array < string | RegExp > ) {
2024-05-23 09:51:07 +00:00
const results : WebpackRequire [ "m" ] = { } ;
2024-05-03 02:18:12 +00:00
const factories = wreq . m ;
outer :
for ( const id in factories ) {
const factory = factories [ id ] ;
2024-05-24 03:00:36 +00:00
const str : string = String ( factory ) ;
2024-05-03 02:18:12 +00:00
for ( const filter of filters ) {
if ( typeof filter === "string" && ! str . includes ( filter ) ) continue outer ;
if ( filter instanceof RegExp && ! filter . test ( str ) ) continue outer ;
}
results [ id ] = factory ;
}
return results ;
}
/ * *
* Extract a specific module by id into its own Source File . This has no effect on
* the code , it is only useful to be able to look at a specific module without having
* to view a massive file . extract then returns the extracted module so you can jump to it .
* As mentioned above , note that this extracted module is not actually used ,
* so putting breakpoints or similar will have no effect .
* @param id The id of the module to extract
* /
2024-05-23 09:51:07 +00:00
export function extract ( id : PropertyKey ) {
const mod = wreq . m [ id ] ;
2024-05-03 02:18:12 +00:00
if ( ! mod ) return null ;
const code = `
2024-05-23 09:51:07 +00:00
// [EXTRACTED] WebpackModule${String(id)}
2024-05-03 02:18:12 +00:00
// WARNING: This module was extracted to be more easily readable.
// This module is NOT ACTUALLY USED! This means putting breakpoints will have NO EFFECT!!
2024-05-24 03:00:36 +00:00
0 , $ { String ( mod ) }
2024-05-23 09:51:07 +00:00
//# sourceURL=ExtractedWebpackModule${String(id)}
2024-05-03 02:18:12 +00:00
` ;
2024-05-23 09:51:07 +00:00
const extracted : ModuleFactory = ( 0 , eval ) ( code ) ;
return extracted ;
2024-05-03 02:18:12 +00:00
}