Merge branch 'modules-proxy-patches' into immediate-finds-modules-proxy

This commit is contained in:
Nuckyz 2024-05-26 05:20:39 -03:00
commit b580d6dcfb
No known key found for this signature in database
GPG key ID: 440BF8296E1C4AD9
2 changed files with 30 additions and 20 deletions

View file

@ -102,3 +102,8 @@ export function pluralise(amount: number, singular: string, plural = singular +
/** Unconfigurable properties for proxies */ /** Unconfigurable properties for proxies */
export const UNCONFIGURABLE_PROPERTIES = ["arguments", "caller", "prototype"]; export const UNCONFIGURABLE_PROPERTIES = ["arguments", "caller", "prototype"];
export function interpolateIfDefined(strings: TemplateStringsArray, ...args: any[]) {
if (args.some(arg => arg == null)) return "";
return strings.reduce((acc, str, i) => `${acc}${str}${args[i] ?? ""}`, "");
}

View file

@ -6,6 +6,7 @@
import { Settings } from "@api/Settings"; import { Settings } from "@api/Settings";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { interpolateIfDefined } from "@utils/misc";
import { canonicalizeReplacement } from "@utils/patches"; import { canonicalizeReplacement } from "@utils/patches";
import { PatchReplacement } from "@utils/types"; import { PatchReplacement } from "@utils/types";
@ -105,8 +106,8 @@ Reflect.defineProperty(Function.prototype, "m", {
// This ensures we actually got the right ones // This ensures we actually got the right ones
const { stack } = new Error(); const { stack } = new Error();
if ((stack?.includes("discord.com") || stack?.includes("discordapp.com")) && !Array.isArray(moduleFactories)) { if ((stack?.includes("discord.com") || stack?.includes("discordapp.com")) && !Array.isArray(moduleFactories)) {
const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1] ?? ""; const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1];
logger.info("Found Webpack module factories in", fileName); logger.info("Found Webpack module factories" + interpolateIfDefined` in ${fileName}`);
// setImmediate to clear this property setter if this is not the main Webpack // setImmediate to clear this property setter if this is not the main Webpack
// If this is the main Webpack, wreq.m will always be set before the timeout runs // If this is the main Webpack, wreq.m will always be set before the timeout runs
@ -117,7 +118,7 @@ Reflect.defineProperty(Function.prototype, "m", {
set(this: WebpackRequire, bundlePath: WebpackRequire["p"]) { set(this: WebpackRequire, bundlePath: WebpackRequire["p"]) {
if (bundlePath !== "/assets/") return; if (bundlePath !== "/assets/") return;
logger.info(`Main Webpack found in ${fileName}, initializing internal references to WebpackRequire`); logger.info("Main Webpack found" + interpolateIfDefined` in ${fileName}` + ", initializing internal references to WebpackRequire");
_initWebpack(this); _initWebpack(this);
clearTimeout(setterTimeout); clearTimeout(setterTimeout);
@ -160,8 +161,6 @@ Reflect.defineProperty(Function.prototype, "m", {
} }
}); });
let webpackNotInitializedLogged = false;
function patchFactory(id: PropertyKey, factory: ModuleFactory) { function patchFactory(id: PropertyKey, factory: ModuleFactory) {
const originalFactory = factory; const originalFactory = factory;
@ -287,7 +286,7 @@ function patchFactory(id: PropertyKey, factory: ModuleFactory) {
if (!patch.all) patches.splice(i--, 1); if (!patch.all) patches.splice(i--, 1);
} }
const patchedFactory: PatchedModuleFactory = function (module, exports, require) { const patchedFactory: PatchedModuleFactory = function (...args: Parameters<ModuleFactory>) {
for (const moduleFactories of allModuleFactories) { for (const moduleFactories of allModuleFactories) {
Reflect.defineProperty(moduleFactories, id, { Reflect.defineProperty(moduleFactories, id, {
value: patchedFactory.$$vencordOriginal, value: patchedFactory.$$vencordOriginal,
@ -297,38 +296,44 @@ function patchFactory(id: PropertyKey, factory: ModuleFactory) {
}); });
} }
if (wreq == null && IS_DEV) { // eslint-disable-next-line prefer-const
if (!webpackNotInitializedLogged) { let [module, exports, require] = args;
webpackNotInitializedLogged = true;
logger.error("WebpackRequire was not initialized, running modules without patches instead.");
}
return originalFactory.call(this, module, exports, require); // Make sure the require argument is actually the WebpackRequire functioin
if (wreq == null && String(require).includes("exports:{}")) {
const { stack } = new Error();
const webpackInstanceFileName = stack?.match(/\/assets\/(.+?\.js)/)?.[1];
logger.warn(
"WebpackRequire was not initialized, falling back to WebpackRequire passed to the first called patched module factory (" +
`id: ${String(id)}` + interpolateIfDefined`, WebpackInstance origin: ${webpackInstanceFileName}` +
")"
);
_initWebpack(require);
} }
let factoryReturn: unknown; let factoryReturn: unknown;
try { try {
factoryReturn = factory.call(this, module, exports, require); factoryReturn = factory.apply(this, args);
} catch (err) { } catch (err) {
// Just rethrow Discord errors // Just re-throw Discord errors
if (factory === originalFactory) throw err; if (factory === originalFactory) throw err;
logger.error("Error in patched module", err); logger.error("Error in patched module factory", err);
return originalFactory.call(this, module, exports, require); return originalFactory.apply(this, args);
} }
// Webpack sometimes sets the value of module.exports directly, so assign exports to it to make sure we properly handle it // Webpack sometimes sets the value of module.exports directly, so assign exports to it to make sure we properly handle it
exports = module.exports; exports = module?.exports;
if (exports == null) return factoryReturn; if (exports == null) return factoryReturn;
// There are (at the time of writing) 11 modules exporting the window // There are (at the time of writing) 11 modules exporting the window
// Make these non enumerable to improve webpack search performance // Make these non enumerable to improve webpack search performance
if (exports === window && require.c) { if (exports === window && require?.c) {
Reflect.defineProperty(require.c, id, { Reflect.defineProperty(require.c, id, {
value: require.c[id], value: require.c[id],
configurable: true, configurable: true,
enumerable: false, writable: true,
writable: true enumerable: false
}); });
return factoryReturn; return factoryReturn;
} }