Merge branch 'development' into imports
This commit is contained in:
commit
cb08178b77
|
@ -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<string, string>} [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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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<string, string>} [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<FetchResponse>}
|
||||
*/
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
export default require;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue