diff --git a/preload/src/api/fetch.js b/preload/src/api/fetch.js new file mode 100644 index 00000000..b9f105f6 --- /dev/null +++ b/preload/src/api/fetch.js @@ -0,0 +1,130 @@ +import * as https from "https"; +import * as http from "http"; + +const MAX_DEFAULT_REDIRECTS = 20; +const redirectCodes = new Set([301, 302, 307, 308]); + +/** + * @typedef {Object} FetchOptions + * @property {"GET" | "PUT" | "POST" | "DELETE"} [method] - Request method. + * @property {Record} [headers] - Request headers. + * @property {"manual" | "follow"} [redirect] - Whether to follow redirects. + * @property {number} [maxRedirects] - Maximum amount of redirects to be followed. + * @property {AbortSignal} [signal] - Signal to abruptly cancel the request + * @property {Uint8Array | string} [body] - Defines a request body. Data must be serializable. + */ + +/** + * @param {string} url + * @param {FetchOptions} options + */ +export function nativeFetch(url, options) { + let state = "PENDING"; + const data = {content: [], headers: null, statusCode: null, url: url, statusText: "", redirected: false}; + const listeners = new Set(); + const errors = new Set(); + + /** * @param {URL} url */ + const execute = (url, options, redirectCount = 0) => { + const Module = url.protocol === "http" ? http : https; + + const req = Module.request(url.href, { + headers: options.headers ?? {}, + method: options.method ?? "GET" + }, res => { + if (redirectCodes.has(res.statusCode) && res.headers.location && options.redirect !== "manual") { + redirectCount++; + + if (redirectCount >= (options.maxRedirects ?? MAX_DEFAULT_REDIRECTS)) { + state = "ABORTED"; + const error = new Error(`Maximum amount of redirects reached (${options.maxRedirects ?? MAX_DEFAULT_REDIRECTS})`); + errors.forEach(e => e(error)); + + return; + } + + let final; + try { + final = new URL(res.headers.location); + } + catch (error) { + state = "ABORTED"; + errors.forEach(e => e(error)); + return; + } + + for (const [key, value] of new URL(url).searchParams.entries()) { + final.searchParams.set(key, value); + } + + return execute(final, options, redirectCount); + } + + res.on("data", chunk => data.content.push(chunk)); + res.on("end", () => { + data.content = Buffer.concat(data.content); + data.headers = res.headers; + data.statusCode = res.statusCode; + data.url = url.toString(); + data.statusText = res.statusMessage; + data.redirected = redirectCount > 0; + state = "DONE"; + + listeners.forEach(listener => listener()); + }); + res.on("error", error => { + state = "ABORTED"; + errors.forEach(e => e(error)); + }); + }); + + if (options.body) { + try {req.write(options.body)} + catch (error) { + state = "ABORTED"; + errors.forEach(e => e(error)); + } + finally { + req.end(); + } + } + else { + req.end(); + } + + if (options.signal) { + options.signal.addEventListener("abort", () => { + req.end(); + state = "ABORTED"; + }); + } + }; + + try { + const parsed = new URL(url); + execute(parsed, options); + } + catch (error) { + state = "ABORTED"; + errors.forEach(e => e(error)); + } + + return { + onComplete(listener) { + listeners.add(listener); + }, + onError(listener) { + errors.add(listener); + }, + readData() { + switch (state) { + case "PENDING": + throw new Error("Cannot read data before request is done!"); + case "ABORTED": + throw new Error("Request was aborted."); + case "DONE": + return data; + } + } + }; +} diff --git a/preload/src/api/https.js b/preload/src/api/https.js index 42713097..e2e6ae0a 100644 --- a/preload/src/api/https.js +++ b/preload/src/api/https.js @@ -34,7 +34,15 @@ const makeRequest = (url, options, callback, setReq) => { req.end(); }); }); - req.end(); + + if (options.formData) { + // Make sure to close the socket. + try {req.write(options.formData);} + finally {req.end();} + } else { + req.end(); + } + }; const request = function (url, options, callback) { diff --git a/preload/src/api/index.js b/preload/src/api/index.js index 2d097c76..7907775f 100644 --- a/preload/src/api/index.js +++ b/preload/src/api/index.js @@ -3,6 +3,7 @@ export {default as https} from "./https"; export * as electron from "./electron"; export * as crypto from "./crypto"; export * as vm from "./vm"; +export * from "./fetch"; // We can expose that without any issues. export * as path from "path"; diff --git a/renderer/src/builtins/window/removeminimumsize.js b/renderer/src/builtins/window/removeminimumsize.js index 5c6cecbd..6f8effd7 100644 --- a/renderer/src/builtins/window/removeminimumsize.js +++ b/renderer/src/builtins/window/removeminimumsize.js @@ -16,7 +16,7 @@ export default new class RemoveMinimumSize extends Builtin { window.addEventListener("resize", this.onResize); const winprefs = DataStore.getData("windowprefs"); - if (!winprefs.height || !winprefs.width) return; // If the values don't exist exit + if (!winprefs.height || !winprefs.width) return DataStore.setData("windowprefs", {}); // If the values don't exist exit and initialize if ((winprefs.height >= DISCORD_MIN_HEIGHT) && (winprefs.width >= DISCORD_MIN_WIDTH)) return; // If both values are normally valid don't touch IPC.setWindowSize(winprefs.width, winprefs.height); } diff --git a/renderer/src/modules/addonmanager.js b/renderer/src/modules/addonmanager.js index 9f2b6a1f..365445b2 100644 --- a/renderer/src/modules/addonmanager.js +++ b/renderer/src/modules/addonmanager.js @@ -223,7 +223,7 @@ export default class AddonManager { return error; } - if (shouldToast) Toasts.success(Strings.Addons.wasUnloaded.format({name: addon.name, version: addon.version})); + if (shouldToast) Toasts.success(Strings.Addons.wasLoaded.format({name: addon.name, version: addon.version})); this.emit("loaded", addon); if (!this.state[addon.id]) return this.state[addon.id] = false; diff --git a/renderer/src/modules/api/contextmenu.js b/renderer/src/modules/api/contextmenu.js index 692ae9b3..2af01498 100644 --- a/renderer/src/modules/api/contextmenu.js +++ b/renderer/src/modules/api/contextmenu.js @@ -23,7 +23,7 @@ const ContextMenuActions = (() => { const out = {}; try { - const ActionsModule = WebpackModules.getModule(m => Object.values(m).some(v => typeof v === "function" && v.toString().includes("CONTEXT_MENU_CLOSE")), {searchExports: false}); + const ActionsModule = WebpackModules.getModule((mod, target, id) => WebpackModules.require.m[id]?.toString().includes(`type:"CONTEXT_MENU_OPEN"`), {searchExports: false}); for (const key of Object.keys(ActionsModule)) { if (ActionsModule[key].toString().includes("CONTEXT_MENU_CLOSE")) { @@ -57,7 +57,7 @@ class MenuPatcher { if (!startupComplete) return Logger.warn("ContextMenu~Patcher", "Startup wasn't successfully, aborting initialization."); const {module, key} = (() => { - const foundModule = WebpackModules.getModule(m => Object.values(m).some(v => typeof v === "function" && v.toString().includes("CONTEXT_MENU_CLOSE")), {searchExports: false}); + const foundModule = WebpackModules.getModule(m => Object.values(m).some(v => typeof v === "function" && v.toString().includes(`type:"CONTEXT_MENU_CLOSE"`)), {searchExports: false}); const foundKey = Object.keys(foundModule).find(k => foundModule[k].length === 3); return {module: foundModule, key: foundKey}; diff --git a/renderer/src/modules/api/fetch.js b/renderer/src/modules/api/fetch.js new file mode 100644 index 00000000..7138a15f --- /dev/null +++ b/renderer/src/modules/api/fetch.js @@ -0,0 +1,85 @@ +import Remote from "../../polyfill/remote"; + +const methods = new Set(["GET", "PUT", "POST", "DELETE"]); + +class FetchResponse extends Response { + constructor(options) { + super(options.content, { + headers: new Headers(options.headers), + method: options.method ?? "GET", + body: options.content, + ...options + }); + + this._options = options; + } + + get url() {return this._options.url;} + get redirected() {return this._options.redirected;} +} + +const convertSignal = signal => { + const listeners = new Set(); + + signal.addEventListener("abort", () => { + listeners.forEach(l => l()); + }); + + return { + addEventListener(_, listener) { + listeners.add(listener); + } + }; +}; + +/** + * @typedef {Object} FetchOptions + * @property {"GET" | "PUT" | "POST" | "DELETE"} [method] - Request method. + * @property {Record} [headers] - Request headers. + * @property {"manual" | "follow"} [redirect] - Whether to follow redirects. + * @property {number} [maxRedirects] - Maximum amount of redirects to be followed. + * @property {AbortSignal} [signal] - Signal to abruptly cancel the request + * @property {Uint8Array | string} [body] - Defines a request body. Data must be serializable. + */ + +/** + * @param {string} url + * @param {FetchOptions} options + * @returns {Promise} + */ +export default function fetch(url, options = {}) { + return new Promise((resolve, reject) => { + const data = {}; + + if (typeof options.headers === "object") { + data.headers = options.headers instanceof Headers ? Object.fromEntries(options.headers.entries()) : options.headers; + } + + if (typeof options.body === "string" || options.body instanceof Uint8Array) data.body = options.body; + if (typeof options.method === "string" && methods.has(options.method)) data.method = options.method; + if (options.signal instanceof AbortSignal) data.signal = convertSignal(options.signal); + + const ctx = Remote.nativeFetch(url, data); + + ctx.onError(error => { + reject(error); + }); + + ctx.onComplete(() => { + try { + const data = ctx.readData(); + + const req = new FetchResponse({ + method: options.method ?? "GET", + status: data.statusCode, + ...options, + ...data + }); + + resolve(req); + } catch (error) { + reject(error); + } + }); + }); +} diff --git a/renderer/src/modules/api/index.js b/renderer/src/modules/api/index.js index 8144d029..37cb7fc3 100644 --- a/renderer/src/modules/api/index.js +++ b/renderer/src/modules/api/index.js @@ -14,7 +14,7 @@ import Utils from "./utils"; import Webpack from "./webpack"; import * as Legacy from "./legacy"; import ContextMenu from "./contextmenu"; - +import fetch from "./fetch"; const bounded = new Map(); const PluginAPI = new AddonAPI(PluginManager); @@ -59,6 +59,7 @@ export default class BdApi { Components = { get Tooltip() {return DiscordModules.Tooltip;} } + Net = {fetch}; } // Add legacy functions @@ -128,6 +129,9 @@ BdApi.Components = { get Tooltip() {return DiscordModules.Tooltip;} }; +BdApi.Net = {fetch}; + Object.freeze(BdApi); +Object.freeze(BdApi.Net); Object.freeze(BdApi.prototype); Object.freeze(BdApi.Components); diff --git a/renderer/src/modules/api/webpack.js b/renderer/src/modules/api/webpack.js index 76f609ff..c6f27887 100644 --- a/renderer/src/modules/api/webpack.js +++ b/renderer/src/modules/api/webpack.js @@ -3,6 +3,18 @@ import Logger from "@common/logger"; import WebpackModules, {Filters} from "@modules/webpackmodules"; +const getOptions = (args, defaultOptions = {}) => { + if (args.length > 1 && + typeof(args[args.length - 1]) === "object" && + !Array.isArray(args[args.length - 1]) && + args[args.length - 1] !== null + ) { + Object.assign(defaultOptions, args.pop()); + } + + return defaultOptions; +}; + /** * `Webpack` is a utility class for getting internal webpack modules. Instance is accessible through the {@link BdApi}. * This is extremely useful for interacting with the internals of Discord. @@ -11,6 +23,10 @@ import WebpackModules, {Filters} from "@modules/webpackmodules"; * @name Webpack */ const Webpack = { + /** + * A Proxy that returns the module source by ID. + */ + modules: WebpackModules.modules, /** * Series of {@link Filters} to be used for finding webpack modules. @@ -18,19 +34,29 @@ const Webpack = { * @memberof Webpack */ Filters: { + /** + * @deprecated + */ + byProps(...props) {return Filters.byKeys(props);}, + /** * Generates a function that filters by a set of properties. - * @param {...string} props List of property names + * @param {...string} keys List of property names * @returns {function} A filter that checks for a set of properties */ - byProps(...props) {return Filters.byProps(props);}, + byKeys(...keys) {return Filters.byKeys(keys);}, + + /** + * @deprecated + */ + byPrototypeFields(...props) {return Filters.byPrototypeKeys(props);}, /** * Generates a function that filters by a set of properties on the object's prototype. * @param {...string} props List of property names * @returns {function} A filter that checks for a set of properties on the object's prototype. */ - byPrototypeFields(...props) {return Filters.byPrototypeFields(props);}, + byPrototypeKeys(...props) {return Filters.byPrototypeKeys(props);}, /** * Generates a function that filters by a regex. @@ -54,6 +80,13 @@ const Webpack = { */ byDisplayName(name) {return Filters.byDisplayName(name);}, + /** + * Generates a function that filters by a specific internal Store name. + * @param {string} name Name the store should have + * @returns {function} A filter that checks for a Store name match + */ + byStoreName(name) {return Filters.byStoreName(name);}, + /** * Generates a combined function from a list of filters. * @param {...function} filters A list of filters @@ -62,6 +95,22 @@ const Webpack = { combine(...filters) {return Filters.combine(...filters);}, }, + /** + * Searches for a module by value, returns module & matched key. Useful in combination with the Patcher. + * @param {(value: any, index: number, array: any[]) => boolean} filter A function to use to filter the module + * @param {object} [options] Set of options to customize the search + * @param {any} [options.target=null] Optional module target to look inside. + * @param {Boolean} [options.defaultExport=true] Whether to return default export when matching the default export + * @param {Boolean} [options.searchExports=false] Whether to execute the filter on webpack export getters. + * @return {[Any, string]} + */ + getWithKey(filter, options = {}) { + if (("first" in options)) return Logger.error("BdApi.Webpack~getWithKey", "Unsupported option first."); + if (("defaultExport" in options) && typeof(options.defaultExport) !== "boolean") return Logger.error("BdApi.Webpack~getWithKey", "Unsupported type used for options.defaultExport", options.defaultExport, "boolean expected."); + if (("searchExports" in options) && typeof(options.searchExports) !== "boolean") return Logger.error("BdApi.Webpack~getWithKey", "Unsupported type used for options.searchExports", options.searchExports, "boolean expected."); + return WebpackModules.getWithKey(filter, options); + }, + /** * Finds a module using a filter function. * @memberof Webpack @@ -73,12 +122,26 @@ const Webpack = { * @return {any} */ getModule(filter, options = {}) { - 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 (("first" in options) && typeof(options.first) !== "boolean") return Logger.error("BdApi.Webpack~get", "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."); if (("searchExports" in options) && typeof(options.searchExports) !== "boolean") return Logger.error("BdApi.Webpack~getModule", "Unsupported type used for options.searchExports", options.searchExports, "boolean expected."); return WebpackModules.getModule(filter, options); }, + /** + * Finds all modules matching a filter function. + * @param {Function} filter A function to use to filter modules + * @param {object} [options] Options to configure the search + * @param {Boolean} [options.defaultExport=true] Whether to return default export when matching the default export + * @param {Boolean} [options.searchExports=false] Whether to execute the filter on webpack exports + * @return {any[]} + */ + getModules(filter, options = {}) { + if (("defaultExport" in options) && typeof(options.defaultExport) !== "boolean") return Logger.error("BdApi.Webpack~getModules", "Unsupported type used for options.defaultExport", options.defaultExport, "boolean expected."); + if (("searchExports" in options) && typeof(options.searchExports) !== "boolean") return Logger.error("BdApi.Webpack~getModules", "Unsupported type used for options.searchExports", options.searchExports, "boolean expected."); + return WebpackModules.getModule(filter, Object.assign(options, {first: false})); + }, + /** * Finds multiple modules using multiple filters. * @memberof Webpack @@ -104,9 +167,106 @@ const Webpack = { waitForModule(filter, options = {}) { 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."); - if (("searchExports" in options) && typeof(options.searchExports) !== "boolean") return Logger.error("BdApi.Webpack~getModule", "Unsupported type used for options.searchExports", options.searchExports, "boolean expected."); + if (("searchExports" in options) && typeof(options.searchExports) !== "boolean") return Logger.error("BdApi.Webpack~waitForModule", "Unsupported type used for options.searchExports", options.searchExports, "boolean expected."); return WebpackModules.getLazy(filter, options); }, + + /** + * Finds a module using its code. + * @param {RegEx} regex A regular expression to use to filter modules + * @param {object} [options] Options to configure the search + * @param {Boolean} [options.defaultExport=true] Whether to return default export when matching the default export + * @param {Boolean} [options.searchExports=false] Whether to execute the filter on webpack exports + * @return {Any} + */ + getByRegex(regex, options = {}) { + return WebpackModules.getModule(Filters.byRegex(regex), options); + }, + + /** + * Finds all modules using its code. + * @param {RegEx} regex A regular expression to use to filter modules + * @param {object} [options] Options to configure the search + * @param {Boolean} [options.defaultExport=true] Whether to return default export when matching the default export + * @param {Boolean} [options.searchExports=false] Whether to execute the filter on webpack exports + * @return {Any[]} + */ + getAllByRegex(regex, options = {}) { + return WebpackModules.getModule(Filters.byRegex(regex), Object.assign({}, options, {first: true})); + }, + + /** + * Finds a single module using properties on its prototype. + * @param {...string} prototypes Properties to use to filter modules + * @return {Any} + */ + getByPrototypeKeys(...prototypes) { + const options = getOptions(prototypes); + + return WebpackModules.getModule(Filters.byPrototypeKeys(prototypes), options); + }, + + /** + * Finds all modules with a set of properties of its prototype. + * @param {...string} prototypes Properties to use to filter modules + * @return {Any[]} + */ + getAllByPrototypeKeys(...prototypes) { + const options = getOptions(prototypes, {first: false}); + + return WebpackModules.getModule(Filters.byPrototypeKeys(prototypes), options); + }, + + /** + * Finds a single module using its own properties. + * @param {...string} props Properties to use to filter modules + * @return {Any} + */ + getByKeys(...props) { + const options = getOptions(props); + + return WebpackModules.getModule(Filters.byKeys(props), options); + }, + + /** + * Finds all modules with a set of properties. + * @param {...string} props Properties to use to filter modules + * @return {Any[]} + */ + getAllByKeys(...props) { + const options = getOptions(props, {first: false}); + + return WebpackModules.getModule(Filters.byKeys(props), options); + }, + + /** + * Finds a single module using a set of strings. + * @param {...String} props Strings to use to filter modules + * @return {Any} + */ + getByStrings(...strings) { + const options = getOptions(strings); + + return WebpackModules.getModule(Filters.byStrings(...strings), options); + }, + + /** + * Finds all modules with a set of strings. + * @param {...String} strings Strings to use to filter modules + * @return {Any[]} + */ + getAllByStrings(...strings) { + const options = getOptions(strings, {first: false}); + + return WebpackModules.getModule(Filters.byStrings(...strings), options); + }, + + /** + * Finds an internal Store module using the name. + * @param {String} name Name of the store to find (usually includes "Store") + * @return {Any} + */ + getStore(name) {return WebpackModules.getModule(Filters.byStoreName(name));}, }; Object.freeze(Webpack); diff --git a/renderer/src/modules/discordmodules.js b/renderer/src/modules/discordmodules.js index acf56bad..8eea820f 100644 --- a/renderer/src/modules/discordmodules.js +++ b/renderer/src/modules/discordmodules.js @@ -161,6 +161,6 @@ export default Utilities.memoizeObject({ // Make fallback component just pass children, so it can at least render that. const fallback = props => props.children?.({}) ?? null; - return WebpackModules.getModule(Filters.byPrototypeFields(["renderTooltip"]), {searchExports: true}) ?? fallback; + return WebpackModules.getModule(Filters.byPrototypeKeys(["renderTooltip"]), {searchExports: true}) ?? fallback; } }); diff --git a/renderer/src/modules/webpackmodules.js b/renderer/src/modules/webpackmodules.js index 936ffc9c..dc1fdea4 100644 --- a/renderer/src/modules/webpackmodules.js +++ b/renderer/src/modules/webpackmodules.js @@ -22,7 +22,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 byProps(props, filter = m => m) { + static byKeys(props, filter = m => m) { return module => { if (!module) return false; if (typeof(module) !== "object" && typeof(module) !== "function") return false; @@ -41,7 +41,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 on the object's prototype */ - static byPrototypeFields(fields, filter = m => m) { + static byPrototypeKeys(fields, filter = m => m) { return module => { if (!module) return false; if (typeof(module) !== "object" && typeof(module) !== "function") return false; @@ -94,7 +94,6 @@ export class Filters { /** * Generates a {@link module:WebpackModules.Filters~filter} that filters by a set of properties. * @param {string} name - Name the module should have - * @param {module:WebpackModules.Filters~filter} filter - Additional filter * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties */ static byDisplayName(name) { @@ -103,6 +102,17 @@ export class Filters { }; } + /** + * Generates a {@link module:WebpackModules.Filters~filter} that filters by a set of properties. + * @param {string} name - Name the store should have (usually includes the word Store) + * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties + */ + static byStoreName(name) { + return module => { + return module?._dispatchToken && module?.getName?.() === name; + }; + } + /** * Generates a combined {@link module:WebpackModules.Filters~filter} from a list of filters. * @param {...module:WebpackModules.Filters~filter} filters - A list of filters @@ -140,6 +150,25 @@ export default class WebpackModules { static findByUniqueProperties(props, first = true) {return first ? this.getByProps(...props) : this.getAllByProps(...props);} static findByDisplayName(name) {return this.getByDisplayName(name);} + /** + * A Proxy that returns the module source by ID. + */ + static modules = new Proxy({}, { + ownKeys() {return Object.keys(WebpackModules.require.m);}, + getOwnPropertyDescriptor() { + return { + enumerable: true, + configurable: true, // Not actually + }; + }, + get(_, k) { + return WebpackModules.require.m[k]; + }, + set() { + throw new Error("[WebpackModules~modules] Setting modules is not allowed."); + } + }); + /** * Finds a module using a filter function. * @param {function} filter A function to use to filter modules @@ -252,6 +281,24 @@ export default class WebpackModules { return returnedModules; } + /** + * Searches for a module by value, returns module & matched key. Useful in combination with the Patcher. + * @param {(value: any, index: number, array: any[]) => boolean} filter A function to use to filter the module + * @param {object} [options] Set of options to customize the search + * @param {any} [options.target=null] Optional module target to look inside. + * @param {Boolean} [options.defaultExport=true] Whether to return default export when matching the default export + * @param {Boolean} [options.searchExports=false] Whether to execute the filter on webpack export getters. + * @return {[Any, string]} + */ + static *getWithKey(filter, {target = null, ...rest} = {}) { + yield target ??= this.getModule(exports => + Object.values(exports).some(filter), + rest + ); + + yield target && Object.keys(target).find(k => filter(target[k])); + } + /** * Finds all modules matching a filter function. * @param {Function} filter A function to use to filter modules @@ -283,7 +330,7 @@ export default class WebpackModules { * @return {Any} */ static getByPrototypes(...prototypes) { - return this.getModule(Filters.byPrototypeFields(prototypes)); + return this.getModule(Filters.byPrototypeKeys(prototypes)); } /** @@ -292,7 +339,7 @@ export default class WebpackModules { * @return {Any} */ static getAllByPrototypes(...prototypes) { - return this.getModule(Filters.byPrototypeFields(prototypes), {first: false}); + return this.getModule(Filters.byPrototypeKeys(prototypes), {first: false}); } /** @@ -301,7 +348,7 @@ export default class WebpackModules { * @return {Any} */ static getByProps(...props) { - return this.getModule(Filters.byProps(props)); + return this.getModule(Filters.byKeys(props)); } /** @@ -310,7 +357,7 @@ export default class WebpackModules { * @return {Any} */ static getAllByProps(...props) { - return this.getModule(Filters.byProps(props), {first: false}); + return this.getModule(Filters.byKeys(props), {first: false}); } /** diff --git a/renderer/src/polyfill/index.js b/renderer/src/polyfill/index.js index 0bddb075..6e772ee4 100644 --- a/renderer/src/polyfill/index.js +++ b/renderer/src/polyfill/index.js @@ -8,6 +8,12 @@ import https from "./https"; import Buffer from "./buffer"; import crypto from "./crypto"; import Remote from "./remote"; +import Logger from "common/logger"; + +const deprecated = new Map([ + ["request", "Use BdApi.Net.fetch instead."], + ["https", "Use BdApi.Net.fetch instead."], +]); const originalFs = Object.assign({}, fs); @@ -16,6 +22,10 @@ originalFs.writeFile = (path, data, options) => fs.writeFile(path, data, Object. export const createRequire = function (path) { return mod => { + if (deprecated.has(mod)) { + Logger.warn("Remote~Require", `The "${mod}" module is marked as deprecated. ${deprecated.get(mod)}`); + } + switch (mod) { case "request": return request; case "https": return https; @@ -44,4 +54,4 @@ require.resolve = (path) => { } }; -export default require; \ No newline at end of file +export default require; diff --git a/renderer/src/ui/settings.js b/renderer/src/ui/settings.js index 2f566f6d..c54d3c07 100644 --- a/renderer/src/ui/settings.js +++ b/renderer/src/ui/settings.js @@ -67,7 +67,7 @@ export default new class SettingsRenderer { } async patchSections() { - const UserSettings = await WebpackModules.getLazy(Filters.byPrototypeFields(["getPredicateSections"])); + const UserSettings = await WebpackModules.getLazy(Filters.byPrototypeKeys(["getPredicateSections"])); Patcher.after("SettingsManager", UserSettings.prototype, "getPredicateSections", (thisObject, args, returnValue) => { let location = returnValue.findIndex(s => s.section.toLowerCase() == "changelog") - 1;