Take a different approach which requires less cursed code
This commit is contained in:
parent
b38ab066d9
commit
9bbec66ea5
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
import { Settings } from "@api/Settings";
|
import { Settings } from "@api/Settings";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { UNCONFIGURABLE_PROPERTIES } from "@utils/misc";
|
|
||||||
import { canonicalizeMatch, canonicalizeReplacement } from "@utils/patches";
|
import { canonicalizeMatch, canonicalizeReplacement } from "@utils/patches";
|
||||||
import { PatchReplacement } from "@utils/types";
|
import { PatchReplacement } from "@utils/types";
|
||||||
|
|
||||||
|
@ -17,34 +16,36 @@ import { _initWebpack, beforeInitListeners, factoryListeners, ModuleFactory, mod
|
||||||
const logger = new Logger("WebpackInterceptor", "#8caaee");
|
const logger = new Logger("WebpackInterceptor", "#8caaee");
|
||||||
const initCallbackRegex = canonicalizeMatch(/{return \i\(".+?"\)}/);
|
const initCallbackRegex = canonicalizeMatch(/{return \i\(".+?"\)}/);
|
||||||
|
|
||||||
const allProxiedModules = new Set<WebpackRequire["m"]>();
|
/** A set with all the module factories objects */
|
||||||
|
const allModuleFactories = new Set<WebpackRequire["m"]>();
|
||||||
|
|
||||||
const modulesProxyHandler: ProxyHandler<WebpackRequire["m"]> = {
|
function defineModuleFactoryGetter(modules: WebpackRequire["m"], id: PropertyKey, factory: ModuleFactory) {
|
||||||
...Object.fromEntries(Object.getOwnPropertyNames(Reflect).map(propName =>
|
Object.defineProperty(modules, id, {
|
||||||
[propName, (...args: any[]) => Reflect[propName](...args)]
|
get: () => {
|
||||||
)),
|
// $$vencordOriginal means the factory is already patched
|
||||||
get: (target, p) => {
|
|
||||||
const propValue = Reflect.get(target, p, target);
|
|
||||||
|
|
||||||
// If the property is not a number, we are not dealing with a module factory
|
|
||||||
// $$vencordOriginal means the factory is already patched, $$vencordRequired means it has already been required
|
|
||||||
// and replaced with the original
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (propValue == null || Number.isNaN(Number(p)) || propValue.$$vencordOriginal != null || propValue.$$vencordRequired === true) {
|
if (factory.$$vencordOriginal != null) {
|
||||||
return propValue;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This patches factories if eagerPatches are disabled
|
// This patches factories if eagerPatches are disabled
|
||||||
const patchedFactory = patchFactory(p, propValue);
|
return (factory = patchFactory(id, factory));
|
||||||
Reflect.set(target, p, patchedFactory, target);
|
|
||||||
|
|
||||||
return patchedFactory;
|
|
||||||
},
|
},
|
||||||
set: (target, p, newValue) => {
|
configurable: true,
|
||||||
// $$vencordRequired means we are resetting the factory to its original after being required
|
enumerable: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleFactoriesHandler: ProxyHandler<WebpackRequire["m"]> = {
|
||||||
|
set: (target, p, newValue, receiver) => {
|
||||||
// If the property is not a number, we are not dealing with a module factory
|
// If the property is not a number, we are not dealing with a module factory
|
||||||
if (!Settings.eagerPatches || newValue?.$$vencordRequired === true || Number.isNaN(Number(p))) {
|
if (Number.isNaN(Number(p))) {
|
||||||
return Reflect.set(target, p, newValue, target);
|
return Reflect.set(target, p, newValue, receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Settings.eagerPatches) {
|
||||||
|
defineModuleFactoryGetter(target, p, newValue);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingFactory = Reflect.get(target, p, target);
|
const existingFactory = Reflect.get(target, p, target);
|
||||||
|
@ -58,18 +59,16 @@ const modulesProxyHandler: ProxyHandler<WebpackRequire["m"]> = {
|
||||||
const patchedFactory = patchFactory(p, newValue);
|
const patchedFactory = patchFactory(p, newValue);
|
||||||
|
|
||||||
// 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 proxiedModules of allProxiedModules) {
|
for (const moduleFactories of allModuleFactories) {
|
||||||
Reflect.set(proxiedModules, p, patchedFactory, proxiedModules);
|
Object.defineProperty(moduleFactories, p, {
|
||||||
|
value: patchedFactory,
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
writable: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
|
||||||
ownKeys: target => {
|
|
||||||
const keys = Reflect.ownKeys(target);
|
|
||||||
for (const key of UNCONFIGURABLE_PROPERTIES) {
|
|
||||||
if (!keys.includes(key)) keys.push(key);
|
|
||||||
}
|
|
||||||
return keys;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -154,43 +153,35 @@ Object.defineProperty(Function.prototype, "O", {
|
||||||
// wreq.m is the webpack object containing module factories.
|
// wreq.m is the webpack object containing module factories.
|
||||||
// This is pre-populated with module factories, and is also populated via webpackGlobal.push
|
// This is pre-populated with module factories, and is also populated via webpackGlobal.push
|
||||||
// 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 replace its prototype with our proxy, which is responsible for patching the module factories
|
// We replace wrap it with our proxy, which is responsible for patching the module factories, or setting up getters for them
|
||||||
Object.defineProperty(Function.prototype, "m", {
|
Object.defineProperty(Function.prototype, "m", {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
|
|
||||||
set(this: WebpackRequire, originalModules: WebpackRequire["m"]) {
|
set(this: WebpackRequire, moduleFactories: WebpackRequire["m"]) {
|
||||||
// When using react devtools or other extensions, we may also catch their webpack here.
|
// When using react devtools or other extensions, we may also catch their webpack here.
|
||||||
// This ensures we actually got the right one
|
// This ensures we actually got the right one
|
||||||
const { stack } = new Error();
|
const { stack } = new Error();
|
||||||
if ((stack?.includes("discord.com") || stack?.includes("discordapp.com")) && !Array.isArray(originalModules)) {
|
if ((stack?.includes("discord.com") || stack?.includes("discordapp.com")) && !Array.isArray(moduleFactories)) {
|
||||||
logger.info("Found Webpack module factory", stack.match(/\/assets\/(.+?\.js)/)?.[1] ?? "");
|
logger.info("Found Webpack module factory", stack.match(/\/assets\/(.+?\.js)/)?.[1] ?? "");
|
||||||
|
|
||||||
// The new object which will contain the factories
|
for (const id in moduleFactories) {
|
||||||
const proxiedModules: WebpackRequire["m"] = Object.create(Object.getPrototypeOf(originalModules));
|
|
||||||
// @ts-ignore
|
|
||||||
proxiedModules[Symbol.toStringTag] = "ProxiedModules";
|
|
||||||
|
|
||||||
for (const id in originalModules) {
|
|
||||||
// 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) {
|
||||||
proxiedModules[id] = patchFactory(id, originalModules[id]);
|
moduleFactories[id] = patchFactory(id, moduleFactories[id]);
|
||||||
} else {
|
} else {
|
||||||
proxiedModules[id] = originalModules[id];
|
defineModuleFactoryGetter(moduleFactories, id, moduleFactories[id]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the original object so pre-populated factories are patched if eagerPatches are disabled
|
allModuleFactories.add(moduleFactories);
|
||||||
delete originalModules[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
allProxiedModules.add(proxiedModules);
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
originalModules[Symbol.toStringTag] = "OriginalModules";
|
moduleFactories[Symbol.toStringTag] = "ModuleFactories";
|
||||||
Object.setPrototypeOf(originalModules, new Proxy(proxiedModules, modulesProxyHandler));
|
moduleFactories = new Proxy(moduleFactories, moduleFactoriesHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.defineProperty(this, "m", {
|
Object.defineProperty(this, "m", {
|
||||||
value: originalModules,
|
value: moduleFactories,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
writable: true
|
writable: true
|
||||||
|
@ -325,10 +316,13 @@ function patchFactory(id: PropertyKey, factory: ModuleFactory) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const patchedFactory: ModuleFactory = (module, exports, require) => {
|
const patchedFactory: ModuleFactory = (module, exports, require) => {
|
||||||
// @ts-ignore
|
for (const moduleFactories of allModuleFactories) {
|
||||||
originalFactory.$$vencordRequired = true;
|
Object.defineProperty(moduleFactories, id, {
|
||||||
for (const proxiedModules of allProxiedModules) {
|
value: originalFactory,
|
||||||
Reflect.set(proxiedModules, id, originalFactory, proxiedModules);
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
writable: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wreq == null && IS_DEV) {
|
if (wreq == null && IS_DEV) {
|
||||||
|
|
Loading…
Reference in a new issue