export all webpack instances

This commit is contained in:
Nuckyz 2024-05-28 17:11:17 -03:00
parent 9c2545ab16
commit 1eeadbcd97
No known key found for this signature in database
GPG key ID: 440BF8296E1C4AD9
2 changed files with 71 additions and 81 deletions

View file

@ -23,10 +23,10 @@ export * as Util from "./utils";
export * as QuickCss from "./utils/quickCss"; export * as QuickCss from "./utils/quickCss";
export * as Updater from "./utils/updater"; export * as Updater from "./utils/updater";
export * as Webpack from "./webpack"; export * as Webpack from "./webpack";
export * as WebpackPatcher from "./webpack/patchWebpack";
export { PlainSettings, Settings }; export { PlainSettings, Settings };
import "./utils/quickCss"; import "./utils/quickCss";
import "./webpack/patchWebpack";
import { openUpdaterModal } from "@components/VencordSettings/UpdaterTab"; import { openUpdaterModal } from "@components/VencordSettings/UpdaterTab";
import { StartAt } from "@utils/types"; import { StartAt } from "@utils/types";

View file

@ -14,6 +14,8 @@ import { traceFunction } from "../debug/Tracer";
import { patches } from "../plugins"; import { patches } from "../plugins";
import { _initWebpack, factoryListeners, ModuleFactory, moduleListeners, subscriptions, WebpackRequire, wreq } from "."; import { _initWebpack, factoryListeners, ModuleFactory, moduleListeners, subscriptions, WebpackRequire, wreq } from ".";
type AnyWebpackRequire = Partial<WebpackRequire> & Pick<WebpackRequire, "m">;
type PatchedModuleFactory = ModuleFactory & { type PatchedModuleFactory = ModuleFactory & {
$$vencordOriginal?: ModuleFactory; $$vencordOriginal?: ModuleFactory;
}; };
@ -22,31 +24,50 @@ type PatchedModuleFactories = Record<PropertyKey, PatchedModuleFactory>;
const logger = new Logger("WebpackInterceptor", "#8caaee"); const logger = new Logger("WebpackInterceptor", "#8caaee");
/** A set with all the module factories objects */ /** A set with all the Webpack instances */
const allModuleFactories = new Set<PatchedModuleFactories>(); export const allWebpackInstances = new Set<AnyWebpackRequire>();
/** Whether we tried to fallback to factory WebpackRequire, or disabled patches */ /** Whether we tried to fallback to factory WebpackRequire, or disabled patches */
let wreqFallbackApplied = false; let wreqFallbackApplied = false;
type Define = typeof Reflect.defineProperty;
const define: Define = (target, p, attributes) => {
if (Object.hasOwn(attributes, "value")) {
attributes.writable = true;
}
return Reflect.defineProperty(target, p, {
configurable: true,
enumerable: true,
...attributes
});
};
// wreq.m is the Webpack object containing module factories. // wreq.m is the Webpack object containing module factories.
// We wrap it with our proxy, which is responsible for patching the module factories when they are set, or definining getters for the patched versions. // We wrap it with our proxy, which is responsible for patching the module factories when they are set, or definining getters for the patched versions.
// 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.
// wreq.m is pre-populated with module factories, and is also populated via webpackGlobal.push // wreq.m 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 wreq.m, so this also patches the sentry module factories. // The sentry module also has their own Webpack with a pre-populated wreq.m, so this also patches the sentry module factories.
Reflect.defineProperty(Function.prototype, "m", { define(Function.prototype, "m", {
configurable: true, enumerable: false,
set(this: WebpackRequire, moduleFactories: PatchedModuleFactories) { set(this: WebpackRequire, moduleFactories: PatchedModuleFactories) {
// 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 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]; define(this, "m", { value: moduleFactories });
return;
}
const fileName = stack?.match(/\/assets\/(.+?\.js)/)?.[1];
logger.info("Found Webpack module factories" + interpolateIfDefined` in ${fileName}`); logger.info("Found Webpack module factories" + interpolateIfDefined` in ${fileName}`);
allWebpackInstances.add(this);
// Define a setter for the bundlePath property of WebpackRequire. Only the main Webpack has this property. // Define a setter for the bundlePath property of WebpackRequire. Only the main Webpack has this property.
// So if the setter is called, this means we can initialize the internal references to WebpackRequire. // So if the setter is called, this means we can initialize the internal references to WebpackRequire.
Reflect.defineProperty(this, "p", { define(this, "p", {
configurable: true, enumerable: false,
set(this: WebpackRequire, bundlePath: WebpackRequire["p"]) { set(this: WebpackRequire, bundlePath: WebpackRequire["p"]) {
if (bundlePath !== "/assets/") return; if (bundlePath !== "/assets/") return;
@ -55,30 +76,15 @@ Reflect.defineProperty(Function.prototype, "m", {
_initWebpack(this); _initWebpack(this);
clearTimeout(setterTimeout); clearTimeout(setterTimeout);
Reflect.defineProperty(this, "p", { define(this, "p", { value: bundlePath });
value: bundlePath,
configurable: true,
enumerable: true,
writable: true
});
} }
}); });
// 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);
// This needs to be added before the loop below define(moduleFactories, Symbol.toStringTag, {
allModuleFactories.add(moduleFactories);
// Patch the pre-populated factories
for (const id in moduleFactories) {
defineModulesFactoryGetter(id, Settings.eagerPatches ? patchFactory(id, moduleFactories[id]) : moduleFactories[id]);
}
Reflect.defineProperty(moduleFactories, Symbol.toStringTag, {
value: "ModuleFactories", value: "ModuleFactories",
configurable: true,
writable: true,
enumerable: false enumerable: false
}); });
@ -88,17 +94,17 @@ Reflect.defineProperty(Function.prototype, "m", {
If Discord ever decides to set module factories using the variable of the modules object directly, instead of wreq.m, switch the proxy to the prototype If Discord ever decides to set module factories using the variable of the modules object directly, instead of wreq.m, switch the proxy to the prototype
Reflect.setPrototypeOf(moduleFactories, new Proxy(moduleFactories, moduleFactoriesHandler)); Reflect.setPrototypeOf(moduleFactories, new Proxy(moduleFactories, moduleFactoriesHandler));
*/ */
}
Reflect.defineProperty(this, "m", { define(this, "m", { value: moduleFactories });
value: moduleFactories,
configurable: true, // Patch the pre-populated factories
enumerable: true, for (const id in moduleFactories) {
writable: true defineModulesFactoryGetter(id, Settings.eagerPatches ? patchFactory(id, moduleFactories[id]) : moduleFactories[id]);
}); }
} }
}); });
/** /**
* Define the getter for returning the patched version of the module factory. * Define the getter for returning the patched version of the module factory.
* *
@ -111,11 +117,8 @@ Reflect.defineProperty(Function.prototype, "m", {
function defineModulesFactoryGetter(id: PropertyKey, factory: PatchedModuleFactory) { function defineModulesFactoryGetter(id: PropertyKey, factory: PatchedModuleFactory) {
// Define the getter in all the module factories objects. Patches are only executed once, so make sure all module factories object // Define the getter in all the module factories objects. Patches are only executed once, so make sure all module factories object
// have the patched version // have the patched version
for (const moduleFactories of allModuleFactories) { for (const wreq of allWebpackInstances) {
Reflect.defineProperty(moduleFactories, id, { define(wreq.m, id, {
configurable: true,
enumerable: true,
get() { get() {
// $$vencordOriginal means the factory is already patched // $$vencordOriginal means the factory is already patched
if (factory.$$vencordOriginal != null) { if (factory.$$vencordOriginal != null) {
@ -155,13 +158,7 @@ const moduleFactoriesHandler: ProxyHandler<PatchedModuleFactories> = {
set: (target, p, newValue, receiver) => { 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 (Number.isNaN(Number(p))) { if (Number.isNaN(Number(p))) {
Reflect.defineProperty(target, p, { return define(target, p, { value: newValue });
value: newValue,
configurable: true,
enumerable: true,
writable: true
});
return true;
} }
const existingFactory = Reflect.get(target, p, receiver); const existingFactory = Reflect.get(target, p, receiver);
@ -332,13 +329,8 @@ function patchFactory(id: PropertyKey, factory: ModuleFactory) {
PatchedFactory(...args: Parameters<ModuleFactory>) { PatchedFactory(...args: Parameters<ModuleFactory>) {
// Restore the original factory in all the module factories objects, // Restore the original factory in all the module factories objects,
// because we want to make sure the original factory is restored properly, no matter what is the Webpack instance // because we want to make sure the original factory is restored properly, no matter what is the Webpack instance
for (const moduleFactories of allModuleFactories) { for (const wreq of allWebpackInstances) {
Reflect.defineProperty(moduleFactories, id, { define(wreq.m, id, { value: patchedFactory.$$vencordOriginal });
value: patchedFactory.$$vencordOriginal,
configurable: true,
enumerable: true,
writable: true
});
} }
// eslint-disable-next-line prefer-const // eslint-disable-next-line prefer-const
@ -387,10 +379,8 @@ function patchFactory(id: PropertyKey, factory: ModuleFactory) {
// 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 && typeof require === "function" && require.c != null) { if (exports === window && typeof require === "function" && require.c != null) {
Reflect.defineProperty(require.c, id, { define(require.c, id, {
value: require.c[id], value: require.c[id],
configurable: true,
writable: true,
enumerable: false enumerable: false
}); });
return factoryReturn; return factoryReturn;