From 2018f691e64b340531f8c7554e4808131a7aaf88 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Mon, 1 Aug 2022 20:52:07 -0400 Subject: [PATCH] Add getbulk and patcher bind --- renderer/src/modules/addonmanager.js | 2 +- renderer/src/modules/componentpatcher.js | 2 +- renderer/src/modules/discordclasses.js | 2 +- renderer/src/modules/pluginapi.js | 29 ++++++++++-- renderer/src/modules/webpackmodules.js | 60 ++++++++++++++++++++++-- 5 files changed, 85 insertions(+), 10 deletions(-) diff --git a/renderer/src/modules/addonmanager.js b/renderer/src/modules/addonmanager.js index 4891f10c..d0f5207b 100644 --- a/renderer/src/modules/addonmanager.js +++ b/renderer/src/modules/addonmanager.js @@ -155,7 +155,7 @@ export default class AddonManager { for (const line of block.split(splitRegex)) { if (line.length === 0) continue; if (line.charAt(0) === "@" && line.charAt(1) !== " ") { - out[field] = accum; + out[field] = accum.trim(); const l = line.indexOf(" "); field = line.substring(1, l); accum = line.substring(l + 1); diff --git a/renderer/src/modules/componentpatcher.js b/renderer/src/modules/componentpatcher.js index c5b82a48..91b6fe4d 100644 --- a/renderer/src/modules/componentpatcher.js +++ b/renderer/src/modules/componentpatcher.js @@ -135,7 +135,7 @@ export default new class ComponentPatcher { patchGuildPills() { if (this.guildPillPatch) return; - const guildPill = WebpackModules.find(m => m.default.displayName === "AnimatedHalfPill"); + const guildPill = WebpackModules.find(m => m?.default?.displayName === "AnimatedHalfPill"); if (!guildPill) return; this.guildPillPatch = Patcher.after("ComponentPatcher", guildPill, "default", (_, args, returnValue) => { const props = args[0]; diff --git a/renderer/src/modules/discordclasses.js b/renderer/src/modules/discordclasses.js index 5ab7ae38..b8136036 100644 --- a/renderer/src/modules/discordclasses.js +++ b/renderer/src/modules/discordclasses.js @@ -38,7 +38,7 @@ const DiscordClasses = new Proxy(DiscordClassModules, { return new Proxy(list[item], { get(obj, prop) { - if (!Reflect.has(obj, prop)) return ""; + if (!(prop in obj)) return ""; return new ClassName(obj[prop]); } diff --git a/renderer/src/modules/pluginapi.js b/renderer/src/modules/pluginapi.js index f634d447..544ec434 100644 --- a/renderer/src/modules/pluginapi.js +++ b/renderer/src/modules/pluginapi.js @@ -264,6 +264,16 @@ const makeAddonAPI = (manager) => new class AddonAPI { BdApi.Plugins = makeAddonAPI(PluginManager); BdApi.Themes = makeAddonAPI(ThemeManager); BdApi.Patcher = { + bind: (id) => { + return { + patch: BdApi.Patcher.patch.bind(BdApi.Patcher, id), + before: BdApi.Patcher.before.bind(BdApi.Patcher, id), + instead: BdApi.Patcher.instead.bind(BdApi.Patcher, id), + after: BdApi.Patcher.after.bind(BdApi.Patcher, id), + getPatchesByCaller: BdApi.Patcher.getPatchesByCaller.bind(BdApi.Patcher, id), + unpatchAll: BdApi.Patcher.unpatchAll.bind(BdApi.Patcher, id), + }; + }, patch: (caller, moduleToPatch, functionName, callback, options = {}) => { if (typeof(caller) !== "string") return Logger.err("BdApi.Patcher", "Parameter 0 of patch must be a string representing the caller"); if (options.type !== "before" && options.type !== "instead" && options.type !== "after") return Logger.err("BdApi.Patcher", "options.type must be one of: before, instead, after"); @@ -295,11 +305,22 @@ BdApi.Webpack = { * @return {Any} */ getModule(filter, options = {}) { - if (Reflect.has(options, "first") && typeof(options.first) !== "boolean") return Logger.error("BdApi.Webpack~getModule", "Unsupported type used for options.first", options.first, "boolean expected."); - if (Reflect.has(options, "defaultExport") && typeof(options.defaultExport) !== "boolean") return Logger.error("BdApi.Webpack~getModule", "Unsupported type used for options.defaultExport", options.defaultExport, "boolean expected."); + if (("first" in options) && typeof(options.first) !== "boolean") return Logger.error("BdApi.Webpack~getModule", "Unsupported type used for options.first", options.first, "boolean expected."); + if (("defaultExport" in options) && typeof(options.defaultExport) !== "boolean") return Logger.error("BdApi.Webpack~getModule", "Unsupported type used for options.defaultExport", options.defaultExport, "boolean expected."); return WebpackModules.getModule(filter, options); }, + /** + * Finds multiple modules using multiple filters. + * + * @param {...object} queries Whether to return only the first matching module + * @param {Function} queries.filter A function to use to filter modules + * @param {Boolean} [queries.first=true] Whether to return only the first matching module + * @param {Boolean} [queries.defaultExport=true] Whether to return default export when matching the default export + * @return {Any} + */ + getBulk(...queries) {return WebpackModules.getBulk(...queries);}, + /** * Finds a module that lazily loaded. * @param {(exports) => boolean} filter A function to use to filter modules. @@ -309,8 +330,8 @@ BdApi.Webpack = { * @returns {Promise} */ waitForModule(filter, options = {}) { - if (Reflect.has(options, "defaultExport") && typeof(options.defaultExport) !== "boolean") return Logger.error("BdApi.Webpack~waitForModule", "Unsupported type used for options.defaultExport", options.defaultExport, "boolean expected."); - if (Reflect.has(options, "signal") && !(options.signal instanceof AbortSignal)) return Logger.error("BdApi.Webpack~waitForModule", "Unsupported type used for options.signal", options.signal, "AbortSignal expected."); + if (("defaultExport" in options) && typeof(options.defaultExport) !== "boolean") return Logger.error("BdApi.Webpack~waitForModule", "Unsupported type used for options.defaultExport", options.defaultExport, "boolean expected."); + if (("signal" in options) && !(options.signal instanceof AbortSignal)) return Logger.error("BdApi.Webpack~waitForModule", "Unsupported type used for options.signal", options.signal, "AbortSignal expected."); return WebpackModules.getLazy(filter, options); }, }; diff --git a/renderer/src/modules/webpackmodules.js b/renderer/src/modules/webpackmodules.js index 298416c9..e05eae05 100644 --- a/renderer/src/modules/webpackmodules.js +++ b/renderer/src/modules/webpackmodules.js @@ -24,10 +24,12 @@ export class Filters { */ static byProps(props, filter = m => m) { return module => { + if (!module) return false; + if (typeof(module) !== "object" && typeof(module) !== "function") return false; const component = filter(module); if (!component) return false; for (let p = 0; p < props.length; p++) { - if (!Reflect.has(component, props[p])) return false; + if (!(props[p] in component)) return false; } return true; }; @@ -41,11 +43,13 @@ export class Filters { */ static byPrototypeFields(fields, filter = m => m) { return module => { + if (!module) return false; + if (typeof(module) !== "object" && typeof(module) !== "function") return false; const component = filter(module); if (!component) return false; if (!component.prototype) return false; for (let f = 0; f < fields.length; f++) { - if (!Reflect.has(component.prototype, fields[f])) return false; + if (!(fields[f] in component.prototype)) return false; } return true; }; @@ -155,7 +159,9 @@ export default class WebpackModules { }; const modules = this.getAllModules(); const rm = []; - for (const index in modules) { + const indices = Object.keys(modules); + for (let i = 0; i < indices.length; i++) { + const index = indices[i]; if (!modules.hasOwnProperty(index)) continue; const module = modules[index]; const {exports} = module; @@ -172,6 +178,54 @@ export default class WebpackModules { return first || rm.length == 0 ? undefined : rm; } + /** + * Finds multiple modules using multiple filters. + * + * @param {...object} queries Whether to return only the first matching module + * @param {Function} queries.filter A function to use to filter modules + * @param {Boolean} [queries.first=true] Whether to return only the first matching module + * @param {Boolean} [queries.defaultExport=true] Whether to return default export when matching the default export + * @return {Any} + */ + static getBulk(...queries) { + const modules = this.getAllModules(); + const returnedModules = Array(queries.length); + const indices = Object.keys(modules); + for (let i = 0; i < indices.length; i++) { + const index = indices[i]; + if (!modules.hasOwnProperty(index)) continue; + const module = modules[index]; + const {exports} = module; + if (!exports) continue; + + for (let q = 0; q < queries.length; q++) { + const query = queries[q]; + const {filter, first = true, defaultExport = true} = query; + if (first && returnedModules[q]) continue; // If they only want the first, and we already found it, move on + if (!first && !returnedModules[q]) returnedModules[q] = []; // If they want multiple and we haven't setup the subarry, do it now + + const wrappedFilter = (ex, mod, moduleId) => { + try { + return filter(ex, mod, moduleId); + } + catch (err) { + Logger.warn("WebpackModules~getModule", "Module filter threw an exception.", filter, err); + return false; + } + }; + + let foundModule = null; + if (exports.__esModule && exports.default && wrappedFilter(exports.default, module, index)) foundModule = defaultExport ? exports.default : exports; + if (wrappedFilter(exports, module, index)) foundModule = exports; + if (!foundModule) continue; + if (first) returnedModules[q] = protect(foundModule); + else returnedModules[q].push(protect(foundModule)); + } + } + + return returnedModules; + } + /** * Finds all modules matching a filter function. * @param {Function} filter A function to use to filter modules