From 267b37ddc11db545cab8b6085eb76cad27c0bc8f Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Mon, 1 Aug 2022 17:53:43 -0400 Subject: [PATCH] Expose webpack to bdapi --- renderer/src/builtins/customcss.js | 3 +- renderer/src/modules/localemanager.js | 4 +- renderer/src/modules/pluginapi.js | 39 +++++++++++++++++- renderer/src/modules/webpackmodules.js | 56 +++++++++++++++++--------- 4 files changed, 77 insertions(+), 25 deletions(-) diff --git a/renderer/src/builtins/customcss.js b/renderer/src/builtins/customcss.js index aca8e2cb..330b95d9 100644 --- a/renderer/src/builtins/customcss.js +++ b/renderer/src/builtins/customcss.js @@ -9,7 +9,6 @@ const fs = require("fs"); const electron = require("electron"); const UserSettings = WebpackModules.getByProps("updateAccount"); const Dispatcher = WebpackModules.getByProps("dirtyDispatch"); -const ActionTypes = WebpackModules.getByProps("ActionTypes", "ActivityFlags").ActionTypes; export default new class CustomCSS extends Builtin { get name() {return "Custom CSS";} @@ -151,6 +150,6 @@ export default new class CustomCSS extends Builtin { }); this.isDetached = true; UserSettings.close(); - Dispatcher.dispatch({type: ActionTypes.LAYER_POP}); + Dispatcher.dispatch({type: "LAYER_POP"}); } }; \ No newline at end of file diff --git a/renderer/src/modules/localemanager.js b/renderer/src/modules/localemanager.js index 0249cfea..860f7584 100644 --- a/renderer/src/modules/localemanager.js +++ b/renderer/src/modules/localemanager.js @@ -3,7 +3,7 @@ import DiscordModules from "./discordmodules"; import Utilities from "./utilities"; import Events from "./emitter"; -const {Dispatcher, DiscordConstants, UserSettingsStore} = DiscordModules; +const {Dispatcher, UserSettingsStore} = DiscordModules; export default new class LocaleManager { get discordLocale() {return UserSettingsStore.locale;} @@ -16,7 +16,7 @@ export default new class LocaleManager { initialize() { this.setLocale(this.discordLocale); - Dispatcher.subscribe(DiscordConstants.ActionTypes.USER_SETTINGS_UPDATE, ({settings}) => { + Dispatcher.subscribe("USER_SETTINGS_UPDATE", ({settings}) => { const newLocale = settings.locale; if (newLocale && newLocale != this.locale) this.setLocale(newLocale); }); diff --git a/renderer/src/modules/pluginapi.js b/renderer/src/modules/pluginapi.js index 2d01784c..f634d447 100644 --- a/renderer/src/modules/pluginapi.js +++ b/renderer/src/modules/pluginapi.js @@ -1,6 +1,6 @@ import {Config} from "data"; import Utilities from "./utilities"; -import WebpackModules from "./webpackmodules"; +import WebpackModules, {Filters} from "./webpackmodules"; import DiscordModules from "./discordmodules"; import DataStore from "./datastore"; import DOMManager from "./dommanager"; @@ -134,7 +134,7 @@ BdApi.findModule = function(filter) { // Finds module BdApi.findAllModules = function(filter) { - return WebpackModules.getModule(filter, false); + return WebpackModules.getModule(filter, {first: false}); }; // Finds module @@ -282,9 +282,44 @@ BdApi.Patcher = { } }; +BdApi.Webpack = { + + Filters: Filters, + + /** + * Finds a module using a filter function. + * @param {(exports, module, moduleId) => boolean} filter A function to use to filter modules + * @param {object} [options] Whether to return only the first matching module + * @param {Boolean} [options.first=true] Whether to return only the first matching module + * @param {Boolean} [options.defaultExport=true] Whether to return default export when matching the default export + * @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."); + return WebpackModules.getModule(filter, options); + }, + + /** + * Finds a module that lazily loaded. + * @param {(exports) => boolean} filter A function to use to filter modules. + * @param {object} [options] Whether to return only the first matching module + * @param {AbortSignal} [options.signal] AbortSignal of an AbortController to cancel the promise + * @param {Boolean} [options.defaultExport=true] Whether to return default export when matching the default export + * @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."); + return WebpackModules.getLazy(filter, options); + }, +}; + Object.freeze(BdApi); Object.freeze(BdApi.Plugins); Object.freeze(BdApi.Themes); Object.freeze(BdApi.Patcher); +Object.freeze(BdApi.Webpack); +Object.freeze(BdApi.Webpack.Filters); export default BdApi; \ No newline at end of file diff --git a/renderer/src/modules/webpackmodules.js b/renderer/src/modules/webpackmodules.js index 349ca622..298416c9 100644 --- a/renderer/src/modules/webpackmodules.js +++ b/renderer/src/modules/webpackmodules.js @@ -22,12 +22,12 @@ export class Filters { * @param {module:WebpackModules.Filters~filter} filter - Additional filter * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties */ - static byProperties(props, filter = m => m) { + static byProps(props, filter = m => m) { return module => { const component = filter(module); if (!component) return false; for (let p = 0; p < props.length; p++) { - if (component[props[p]] === undefined) return false; + if (!Reflect.has(component, props[p])) return false; } return true; }; @@ -45,7 +45,7 @@ export class Filters { if (!component) return false; if (!component.prototype) return false; for (let f = 0; f < fields.length; f++) { - if (component.prototype[fields[f]] === undefined) return false; + if (!Reflect.has(component.prototype, fields[f])) return false; } return true; }; @@ -57,7 +57,7 @@ export class Filters { * @param {module:WebpackModules.Filters~filter} filter - Additional filter * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties */ - static byCode(search, filter = m => m) { + static byRegex(search, filter = m => m) { return module => { const method = filter(module); if (!method) return false; @@ -73,7 +73,7 @@ export class Filters { * @param {...String} search - A RegExp to check on the module * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of strings */ - static byString(...strings) { + static byStrings(...strings) { return module => { let moduleString = ""; try {moduleString = module.toString([]);} @@ -137,13 +137,21 @@ export default class WebpackModules { /** * Finds a module using a filter function. * @param {Function} filter A function to use to filter modules - * @param {Boolean} first Whether to return only the first matching module + * @param {object} [options] Whether to return only the first matching module + * @param {Boolean} [options.first=true] Whether to return only the first matching module + * @param {Boolean} [options.defaultExport=true] Whether to return default export when matching the default export * @return {Any} */ - static getModule(filter, first = true) { - const wrappedFilter = (m) => { - try {return filter(m);} - catch (err) {return false;} + static getModule(filter, options = {}) { + const {first = true, defaultExport = true} = options; + const wrappedFilter = (exports, module, moduleId) => { + try { + return filter(exports, module, moduleId); + } + catch (err) { + Logger.warn("WebpackModules~getModule", "Module filter threw an exception.", filter, err); + return false; + } }; const modules = this.getAllModules(); const rm = []; @@ -154,8 +162,8 @@ export default class WebpackModules { let foundModule = null; if (!exports) continue; - if (exports.__esModule && exports.default && wrappedFilter(exports.default)) foundModule = exports.default; - if (wrappedFilter(exports)) foundModule = exports; + 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) return protect(foundModule); rm.push(protect(foundModule)); @@ -186,7 +194,7 @@ export default class WebpackModules { * @return {Any} */ static getByRegex(regex, first = true) { - return this.getModule(Filters.byCode(regex), first); + return this.getModule(Filters.byRegex(regex), first); } /** @@ -213,7 +221,7 @@ export default class WebpackModules { * @return {Any} */ static getByProps(...props) { - return this.getModule(Filters.byProperties(props), true); + return this.getModule(Filters.byProps(props), true); } /** @@ -222,7 +230,7 @@ export default class WebpackModules { * @return {Any} */ static getAllByProps(...props) { - return this.getModule(Filters.byProperties(props), false); + return this.getModule(Filters.byProps(props), false); } /** @@ -231,7 +239,7 @@ export default class WebpackModules { * @return {Any} */ static getByString(...strings) { - return this.getModule(Filters.byString(...strings), true); + return this.getModule(Filters.byStrings(...strings), true); } /** @@ -240,15 +248,21 @@ export default class WebpackModules { * @return {Any} */ static getAllByString(...strings) { - return this.getModule(Filters.byString(...strings), false); + return this.getModule(Filters.byStrings(...strings), false); } /** * Finds a module that lazily loaded. * @param {(m) => boolean} filter A function to use to filter modules. + * @param {object} [options] Whether to return only the first matching module + * @param {AbortSignal} [options.signal] AbortSignal of an AbortController to cancel the promise + * @param {Boolean} [options.defaultExport=true] Whether to return default export when matching the default export * @returns {Promise} */ - static getLazy(filter) { + static getLazy(filter, options = {}) { + /** @type {AbortSignal} */ + const abortSignal = options.signal; + const defaultExport = options.defaultExport; const fromCache = this.getModule(filter); if (fromCache) return Promise.resolve(fromCache); @@ -266,10 +280,14 @@ export default class WebpackModules { if (!defaultMatch) return; cancel(); - resolve(m.default); + resolve(defaultExport ? m.default : defaultExport); }; this.addListener(listener); + abortSignal?.addEventListener("abort", () => { + cancel(); + resolve(); + }); }); }