diff options
Diffstat (limited to 'src/webpack/webpack.ts')
-rw-r--r-- | src/webpack/webpack.ts | 183 |
1 files changed, 138 insertions, 45 deletions
diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index 3db4dd2..ba2d559 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -21,6 +21,8 @@ import type { WebpackInstance } from "discord-types/other"; import Logger from "../utils/logger"; import { proxyLazy } from "../utils/proxyLazy"; +const logger = new Logger("Webpack"); + export let _resolveReady: () => void; /** * Fired once a gateway connection to Discord has been established. @@ -51,7 +53,6 @@ export const filters = { }, }; -const logger = new Logger("Webpack"); export const subscriptions = new Map<FilterFn, CallbackFn>(); export const listeners = new Set<CallbackFn>(); @@ -126,51 +127,143 @@ export function findAll(filter: FilterFn, getDefault = true) { } /** - * Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module) - * then maps it into an easily usable module via the specified mappers - * @param code Code snippet - * @param mappers Mappers to create the non mangled exports - * @returns Unmangled exports as specified in mappers - * - * @example mapMangledModule("headerIdIsManaged:", { - * openModal: filters.byCode("headerIdIsManaged:"), - * closeModal: filters.byCode("key==") - * }) - */ -export function mapMangledModule<S extends string>(code: string, mappers: Record<S, FilterFn>): Record<S, any> { - const exports = {} as Record<S, any>; + * Same as {@link find} but in bulk + * @param filterFns Arry 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 + */ +export function bulk(...filterFns: FilterFn[]) { + if (!Array.isArray(filterFns)) + throw new Error("Invalid filters. Expected function[] got " + typeof filterFns); - // search every factory function - for (const id in wreq.m) { - const src = wreq.m[id].toString() as string; - if (src.includes(code)) { - const mod = wreq(id as any as number); - outer: - for (const key in mod) { - const member = mod[key]; - for (const newName in mappers) { - // if the current mapper matches this module - if (mappers[newName](member)) { - exports[newName] = member; + 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"); + } + return find(filterFns[0]); + } + + const filters = filterFns as Array<FilterFn | undefined>; + + let found = 0; + const results = Array(length); + + outer: + for (const key in cache) { + const mod = cache[key]; + if (!mod?.exports) continue; + + for (let j = 0; j < length; j++) { + const filter = filters[j]; + // Already done + if (filter === undefined) continue; + + if (filter(mod.exports)) { + results[j] = mod.exports; + filters[j] = undefined; + if (++found === length) break outer; + break; + } + + if (typeof mod.exports !== "object") + continue; + + if (mod.exports.default && filter(mod.exports.default)) { + results[j] = mod.exports.default; + filters[j] = undefined; + if (++found === length) break outer; + break; + } + + for (const nestedMod in mod.exports) + if (nestedMod.length <= 3) { + const nested = mod.exports[nestedMod]; + if (nested && filter(nested)) { + results[j] = nested; + filters[j] = undefined; + if (++found === length) break outer; continue outer; } } - } - return exports; } } - const err = new Error("Didn't find module matching this code:\n" + code); - if (IS_DEV) - throw err; + if (found !== length) { + const err = new Error(`Got ${length} filters, but only found ${found} modules!`); + if (IS_DEV) { + // Strict behaviour in DevBuilds to fail early and make sure the issue is found + throw err; + } + logger.warn(err); + } + + return results; +} + +/** + * Find the id of a module by its code + * @param code Code + * @returns number or null + */ +export function findModuleId(code: string) { + for (const id in wreq.m) { + if (wreq.m[id].toString().includes(code)) { + return Number(id); + } + } + const err = new Error("Didn't find module with code:\n" + code); + if (IS_DEV) { + // Strict behaviour in DevBuilds to fail early and make sure the issue is found + throw err; + } logger.warn(err); + + return null; +} + +/** + * Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module) + * then maps it into an easily usable module via the specified mappers + * @param code Code snippet + * @param mappers Mappers to create the non mangled exports + * @returns Unmangled exports as specified in mappers + * + * @example mapMangledModule("headerIdIsManaged:", { + * openModal: filters.byCode("headerIdIsManaged:"), + * closeModal: filters.byCode("key==") + * }) + */ +export function mapMangledModule<S extends string>(code: string, mappers: Record<S, FilterFn>): Record<S, any> { + const exports = {} as Record<S, any>; + + const id = findModuleId(code); + if (id === null) + return exports; + + const mod = wreq(id); + outer: + for (const key in mod) { + const member = mod[key]; + for (const newName in mappers) { + // if the current mapper matches this module + if (mappers[newName](member)) { + exports[newName] = member; + continue outer; + } + } + } return exports; } /** - * Same as {@link mapMangledModule} but lazy - */ + * Same as {@link mapMangledModule} but lazy + */ export function mapMangledModuleLazy<S extends string>(code: string, mappers: Record<S, FilterFn>): Record<S, any> { return proxyLazy(() => mapMangledModule(code, mappers)); } @@ -210,11 +303,11 @@ export function removeListener(callback: CallbackFn) { } /** - * 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 - */ + * 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>) { const results = {} as Record<number, Function>; const factories = wreq.m; @@ -233,13 +326,13 @@ export function search(...filters: Array<string | RegExp>) { } /** - * 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 - */ + * 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 + */ export function extract(id: number) { const mod = wreq.m[id] as Function; if (!mod) return null; |