more future proof

This commit is contained in:
Nuckyz 2024-05-26 00:27:13 -03:00
parent 1e55ae5327
commit 9af63b362d
No known key found for this signature in database
GPG key ID: 440BF8296E1C4AD9
2 changed files with 62 additions and 52 deletions

View file

@ -24,8 +24,9 @@ const logger = new Logger("WebpackInterceptor", "#8caaee");
/** A set with all the module factories objects */ /** A set with all the module factories objects */
const allModuleFactories = new Set<PatchedModuleFactories>(); const allModuleFactories = new Set<PatchedModuleFactories>();
function defineModuleFactoryGetter(modulesFactories: PatchedModuleFactories, id: PropertyKey, factory: PatchedModuleFactory) { function defineModulesFactoryGetter(id: PropertyKey, factory: PatchedModuleFactory) {
Object.defineProperty(modulesFactories, id, { for (const moduleFactories of allModuleFactories) {
Reflect.defineProperty(moduleFactories, id, {
configurable: true, configurable: true,
enumerable: true, enumerable: true,
@ -47,6 +48,7 @@ function defineModuleFactoryGetter(modulesFactories: PatchedModuleFactories, id:
} }
}); });
} }
}
const moduleFactoriesHandler: ProxyHandler<PatchedModuleFactories> = { const moduleFactoriesHandler: ProxyHandler<PatchedModuleFactories> = {
set: (target, p, newValue, receiver) => { set: (target, p, newValue, receiver) => {
@ -64,7 +66,7 @@ const moduleFactoriesHandler: ProxyHandler<PatchedModuleFactories> = {
return Reflect.set(target, p, newValue, receiver); return Reflect.set(target, p, newValue, receiver);
} }
defineModuleFactoryGetter(target, p, newValue); defineModulesFactoryGetter(p, newValue);
return true; return true;
} }
@ -78,7 +80,7 @@ const moduleFactoriesHandler: ProxyHandler<PatchedModuleFactories> = {
// Modules are only patched once, so we need to set the patched factory on all the modules // Modules are only patched once, so we need to set the patched factory on all the modules
for (const moduleFactories of allModuleFactories) { for (const moduleFactories of allModuleFactories) {
Object.defineProperty(moduleFactories, p, { Reflect.defineProperty(moduleFactories, p, {
value: patchedFactory, value: patchedFactory,
configurable: true, configurable: true,
enumerable: true, enumerable: true,
@ -95,7 +97,7 @@ const moduleFactoriesHandler: ProxyHandler<PatchedModuleFactories> = {
// The sentry module also has their own Webpack with a pre-populated module factories object, so this also targets that // The sentry module also has their own Webpack with a pre-populated module factories object, so this also targets that
// We wrap it with our proxy, which is responsible for patching the module factories, or setting up getters for them // We wrap it with our proxy, which is responsible for patching the module factories, or setting up getters for them
// If this is the main Webpack, we also set up the internal references to WebpackRequire // If this is the main Webpack, we also set up the internal references to WebpackRequire
Object.defineProperty(Function.prototype, "m", { Reflect.defineProperty(Function.prototype, "m", {
configurable: true, configurable: true,
set(this: WebpackRequire, moduleFactories: PatchedModuleFactories) { set(this: WebpackRequire, moduleFactories: PatchedModuleFactories) {
@ -103,23 +105,24 @@ Object.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)) {
logger.info("Found Webpack module factories", stack.match(/\/assets\/(.+?\.js)/)?.[1] ?? ""); const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1] ?? "";
logger.info("Found Webpack module factories 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
const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "p"), 0); const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "p"), 0);
Object.defineProperty(this, "p", { Reflect.defineProperty(this, "p", {
configurable: true, configurable: true,
set(this: WebpackRequire, v: WebpackRequire["p"]) { set(this: WebpackRequire, bundlePath: WebpackRequire["p"]) {
if (v !== "/assets/") return; if (bundlePath !== "/assets/") return;
logger.info("Main Webpack found, initializing internal references to WebpackRequire"); logger.info(`Main Webpack found in ${fileName}, initializing internal references to WebpackRequire`);
_initWebpack(this); _initWebpack(this);
clearTimeout(setterTimeout); clearTimeout(setterTimeout);
Object.defineProperty(this, "p", { Reflect.defineProperty(this, "p", {
value: v, value: bundlePath,
configurable: true, configurable: true,
enumerable: true, enumerable: true,
writable: true writable: true
@ -127,26 +130,28 @@ Object.defineProperty(Function.prototype, "m", {
} }
}); });
// This needs to be added before the loop below
allModuleFactories.add(moduleFactories);
for (const id in moduleFactories) { for (const id in moduleFactories) {
// If we have eagerPatches enabled we have to patch the pre-populated factories // If we have eagerPatches enabled we have to patch the pre-populated factories
if (Settings.eagerPatches) { if (Settings.eagerPatches) {
moduleFactories[id] = patchFactory(id, moduleFactories[id]); moduleFactories[id] = patchFactory(id, moduleFactories[id]);
} else { } else {
defineModuleFactoryGetter(moduleFactories, id, moduleFactories[id]); defineModulesFactoryGetter(id, moduleFactories[id]);
} }
} }
allModuleFactories.add(moduleFactories); Reflect.defineProperty(moduleFactories, Symbol.toStringTag, {
Object.defineProperty(moduleFactories, Symbol.toStringTag, {
value: "ModuleFactories", value: "ModuleFactories",
configurable: true, configurable: true,
writable: true writable: true,
enumerable: false
}); });
moduleFactories = new Proxy(moduleFactories, moduleFactoriesHandler); moduleFactories = new Proxy(moduleFactories, moduleFactoriesHandler);
} }
Object.defineProperty(this, "m", { Reflect.defineProperty(this, "m", {
value: moduleFactories, value: moduleFactories,
configurable: true, configurable: true,
enumerable: true, enumerable: true,
@ -158,15 +163,16 @@ Object.defineProperty(Function.prototype, "m", {
let webpackNotInitializedLogged = false; let webpackNotInitializedLogged = false;
function patchFactory(id: PropertyKey, factory: ModuleFactory) { function patchFactory(id: PropertyKey, factory: ModuleFactory) {
const originalFactory = factory;
for (const factoryListener of factoryListeners) { for (const factoryListener of factoryListeners) {
try { try {
factoryListener(factory); factoryListener(originalFactory);
} catch (err) { } catch (err) {
logger.error("Error in Webpack factory listener:\n", err, factoryListener); logger.error("Error in Webpack factory listener:\n", err, factoryListener);
} }
} }
const originalFactory = factory;
const patchedBy = new Set<string>(); const patchedBy = new Set<string>();
// Discords Webpack chunks for some ungodly reason contain random // Discords Webpack chunks for some ungodly reason contain random
@ -193,15 +199,15 @@ function patchFactory(id: PropertyKey, factory: ModuleFactory) {
patchedBy.add(patch.plugin); patchedBy.add(patch.plugin);
const executePatch = traceFunction(`patch by ${patch.plugin}`, (match: string | RegExp, replace: string) => code.replace(match, replace)); const executePatch = traceFunction(`patch by ${patch.plugin}`, (match: string | RegExp, replace: string) => code.replace(match, replace));
const previousFactory = factory;
const previousCode = code; const previousCode = code;
const previousFactory = factory;
// We change all patch.replacement to array in plugins/index // We change all patch.replacement to array in plugins/index
for (const replacement of patch.replacement as PatchReplacement[]) { for (const replacement of patch.replacement as PatchReplacement[]) {
if (replacement.predicate && !replacement.predicate()) continue; if (replacement.predicate && !replacement.predicate()) continue;
const lastFactory = factory;
const lastCode = code; const lastCode = code;
const lastFactory = factory;
canonicalizeReplacement(replacement, patch.plugin); canonicalizeReplacement(replacement, patch.plugin);
@ -217,8 +223,8 @@ function patchFactory(id: PropertyKey, factory: ModuleFactory) {
if (patch.group) { if (patch.group) {
logger.warn(`Undoing patch group ${patch.find} by ${patch.plugin} because replacement ${replacement.match} had no effect`); logger.warn(`Undoing patch group ${patch.find} by ${patch.plugin} because replacement ${replacement.match} had no effect`);
factory = previousFactory;
code = previousCode; code = previousCode;
factory = previousFactory;
patchedBy.delete(patch.plugin); patchedBy.delete(patch.plugin);
break; break;
} }
@ -268,13 +274,13 @@ function patchFactory(id: PropertyKey, factory: ModuleFactory) {
if (patch.group) { if (patch.group) {
logger.warn(`Undoing patch group ${patch.find} by ${patch.plugin} because replacement ${replacement.match} errored`); logger.warn(`Undoing patch group ${patch.find} by ${patch.plugin} because replacement ${replacement.match} errored`);
factory = previousFactory;
code = previousCode; code = previousCode;
factory = previousFactory;
break; break;
} }
factory = lastFactory;
code = lastCode; code = lastCode;
factory = lastFactory;
} }
} }
@ -283,7 +289,7 @@ function patchFactory(id: PropertyKey, factory: ModuleFactory) {
const patchedFactory: PatchedModuleFactory = function (module, exports, require) { const patchedFactory: PatchedModuleFactory = function (module, exports, require) {
for (const moduleFactories of allModuleFactories) { for (const moduleFactories of allModuleFactories) {
Object.defineProperty(moduleFactories, id, { Reflect.defineProperty(moduleFactories, id, {
value: patchedFactory.$$vencordOriginal, value: patchedFactory.$$vencordOriginal,
configurable: true, configurable: true,
enumerable: true, enumerable: true,
@ -297,33 +303,34 @@ function patchFactory(id: PropertyKey, factory: ModuleFactory) {
logger.error("WebpackRequire was not initialized, running modules without patches instead."); logger.error("WebpackRequire was not initialized, running modules without patches instead.");
} }
return void originalFactory.call(this, module, exports, require); return originalFactory.call(this, module, exports, require);
} }
let factoryReturn: unknown;
try { try {
factory.call(this, module, exports, require); factoryReturn = factory.call(this, module, exports, require);
} catch (err) { } catch (err) {
// Just rethrow Discord errors // Just rethrow 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", err);
return void originalFactory.call(this, module, exports, require); return originalFactory.call(this, module, exports, require);
} }
// 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; 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) {
Object.defineProperty(require.c, id, { Reflect.defineProperty(require.c, id, {
value: require.c[id], value: require.c[id],
configurable: true, configurable: true,
enumerable: false, enumerable: false,
writable: true writable: true
}); });
return; return factoryReturn;
} }
for (const callback of moduleListeners) { for (const callback of moduleListeners) {
@ -347,6 +354,8 @@ function patchFactory(id: PropertyKey, factory: ModuleFactory) {
logger.error("Error while firing callback for Webpack subscription:\n", err, filter, callback); logger.error("Error while firing callback for Webpack subscription:\n", err, filter, callback);
} }
} }
return factoryReturn;
}; };
patchedFactory.toString = originalFactory.toString.bind(originalFactory); patchedFactory.toString = originalFactory.toString.bind(originalFactory);

View file

@ -78,10 +78,11 @@ export function _initWebpack(webpackRequire: WebpackRequire) {
wreq = webpackRequire; wreq = webpackRequire;
cache = webpackRequire.c; cache = webpackRequire.c;
Object.defineProperty(webpackRequire.c, Symbol.toStringTag, { Reflect.defineProperty(webpackRequire.c, Symbol.toStringTag, {
value: "ModuleCache", value: "ModuleCache",
configurable: true, configurable: true,
writable: true writable: true,
enumerable: false
}); });
} }
@ -506,7 +507,7 @@ export function search(...filters: Array<string | RegExp>) {
outer: outer:
for (const id in factories) { for (const id in factories) {
const factory = factories[id]; const factory = factories[id];
const str: string = String(factory); const str = String(factory);
for (const filter of filters) { for (const filter of filters) {
if (typeof filter === "string" && !str.includes(filter)) continue outer; if (typeof filter === "string" && !str.includes(filter)) continue outer;
if (filter instanceof RegExp && !filter.test(str)) continue outer; if (filter instanceof RegExp && !filter.test(str)) continue outer;