Fix persisting $$vencordPatchedSource when a module is loaded again
This commit is contained in:
parent
e4bc7f2add
commit
e3d9b2001f
|
@ -47,8 +47,8 @@ define(Function.prototype, "m", {
|
||||||
set(this: AnyWebpackRequire, originalModules: AnyWebpackRequire["m"]) {
|
set(this: AnyWebpackRequire, originalModules: AnyWebpackRequire["m"]) {
|
||||||
define(this, "m", { value: originalModules });
|
define(this, "m", { value: originalModules });
|
||||||
|
|
||||||
// Ensure this is one of Discord main WebpackInstances.
|
// Ensure this is one of Discord main Webpack instances.
|
||||||
// We may catch Discord bundled libs, React Devtools or other extensions WebpackInstances here.
|
// We may catch Discord bundled libs, React Devtools or other extensions Webpack instances here.
|
||||||
const { stack } = new Error();
|
const { stack } = new Error();
|
||||||
if (!stack?.includes("http") || stack.match(/at \d+? \(/) || !String(this).includes("exports:{}")) {
|
if (!stack?.includes("http") || stack.match(/at \d+? \(/) || !String(this).includes("exports:{}")) {
|
||||||
return;
|
return;
|
||||||
|
@ -147,21 +147,26 @@ const moduleFactoriesHandler: ProxyHandler<AnyWebpackRequire["m"]> = {
|
||||||
*/
|
*/
|
||||||
function updateExistingFactory(moduleFactoriesTarget: AnyWebpackRequire["m"], id: PropertyKey, newFactory: AnyModuleFactory, ignoreExistingInTarget: boolean = false) {
|
function updateExistingFactory(moduleFactoriesTarget: AnyWebpackRequire["m"], id: PropertyKey, newFactory: AnyModuleFactory, ignoreExistingInTarget: boolean = false) {
|
||||||
let existingFactory: TypedPropertyDescriptor<AnyModuleFactory> | undefined;
|
let existingFactory: TypedPropertyDescriptor<AnyModuleFactory> | undefined;
|
||||||
|
let moduleFactoriesWithFactory: AnyWebpackRequire["m"] | undefined;
|
||||||
for (const wreq of allWebpackInstances) {
|
for (const wreq of allWebpackInstances) {
|
||||||
if (ignoreExistingInTarget && wreq.m === moduleFactoriesTarget) continue;
|
if (ignoreExistingInTarget && wreq.m === moduleFactoriesTarget) continue;
|
||||||
|
|
||||||
if (Reflect.getOwnPropertyDescriptor(wreq.m, id) != null) {
|
if (Reflect.getOwnPropertyDescriptor(wreq.m, id) != null) {
|
||||||
existingFactory = Reflect.getOwnPropertyDescriptor(wreq.m, id);
|
existingFactory = Reflect.getOwnPropertyDescriptor(wreq.m, id);
|
||||||
|
moduleFactoriesWithFactory = wreq.m;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingFactory != null) {
|
if (existingFactory != null) {
|
||||||
// If existingFactory exists in any Webpack instance, it's either wrapped in defineModuleFactoryGetter, or it has already been required.
|
// If existingFactory exists in any Webpack instance, it's either wrapped in defineModuleFactoryGetter, or it has already been required.
|
||||||
// So define the descriptor of it on this current Webpack instance, call Reflect.set with the new original,
|
// So define the descriptor of it on this current Webpack instance (if it doesn't exist already), call Reflect.set with the new original,
|
||||||
// and let the correct logic apply (normal set, or defineModuleFactoryGetter setter)
|
// and let the correct logic apply (normal set, or defineModuleFactoryGetter setter)
|
||||||
|
|
||||||
|
if (moduleFactoriesWithFactory !== moduleFactoriesTarget) {
|
||||||
Reflect.defineProperty(moduleFactoriesTarget, id, existingFactory);
|
Reflect.defineProperty(moduleFactoriesTarget, id, existingFactory);
|
||||||
|
}
|
||||||
|
|
||||||
return Reflect.set(moduleFactoriesTarget, id, newFactory, moduleFactoriesTarget);
|
return Reflect.set(moduleFactoriesTarget, id, newFactory, moduleFactoriesTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,40 +189,44 @@ function notifyFactoryListeners(factory: AnyModuleFactory) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the getter for returning the patched version of the module factory.
|
* Define the getter for returning the patched or original version of the module factory.
|
||||||
|
* This properly handles patching the factory and also making sure $$vencordPatchedSource is persisted in the original factory, if it was later loaded again.
|
||||||
*
|
*
|
||||||
* If eagerPatches is enabled, the factory argument should already be the patched version, else it will be the original
|
* If eagerPatches is enabled, the factory argument should already be the patched version, else it will be the original
|
||||||
* and only be patched when accessed for the first time.
|
* and only be patched when accessed for the first time.
|
||||||
*
|
*
|
||||||
* @param id The id of the module
|
* @param id The id of the module
|
||||||
* @param factory The original or patched module factory
|
* @param factory The original or patched module factory
|
||||||
|
* @param isFactoryRestore Whether we are restoring the original factory after it has been required
|
||||||
*/
|
*/
|
||||||
function defineModulesFactoryGetter(id: PropertyKey, factory: WrappedModuleFactory) {
|
function defineModulesFactoryGetter(id: PropertyKey, factory: WrappedModuleFactory, isFactoryRestore: boolean = false) {
|
||||||
// Define the getter in all the module factories objects. Patches are only executed once, so make sure all module factories object
|
const descriptor: PropertyDescriptor = {
|
||||||
// have the patched version
|
|
||||||
for (const wreq of allWebpackInstances) {
|
|
||||||
define(wreq.m, id, {
|
|
||||||
get() {
|
get() {
|
||||||
// $$vencordOriginal means the factory is already patched
|
// $$vencordOriginal means the factory is already patched
|
||||||
if (factory.$$vencordOriginal != null) {
|
if (factory.$$vencordOriginal != null || isFactoryRestore) {
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (factory = wrapAndPatchFactory(id, factory));
|
return (factory = wrapAndPatchFactory(id, factory));
|
||||||
},
|
},
|
||||||
set(newFactory: AnyModuleFactory) {
|
set(newFactory: AnyModuleFactory) {
|
||||||
if (factory.$$vencordOriginal != null) {
|
|
||||||
factory.toString = newFactory.toString.bind(newFactory);
|
|
||||||
factory.$$vencordOriginal = newFactory;
|
|
||||||
|
|
||||||
if (factory.$$vencordPatchedSource != null) {
|
if (factory.$$vencordPatchedSource != null) {
|
||||||
newFactory.$$vencordPatchedSource = factory.$$vencordPatchedSource;
|
newFactory.$$vencordPatchedSource = factory.$$vencordPatchedSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (factory.$$vencordOriginal != null) {
|
||||||
|
factory.toString = newFactory.toString.bind(newFactory);
|
||||||
|
factory.$$vencordOriginal = newFactory;
|
||||||
} else {
|
} else {
|
||||||
factory = newFactory;
|
factory = newFactory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// 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
|
||||||
|
for (const wreq of allWebpackInstances) {
|
||||||
|
define(wreq.m, id, descriptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,10 +241,8 @@ function wrapAndPatchFactory(id: PropertyKey, originalFactory: AnyModuleFactory)
|
||||||
const patchedFactory = patchFactory(id, originalFactory);
|
const patchedFactory = patchFactory(id, originalFactory);
|
||||||
|
|
||||||
const wrappedFactory: WrappedModuleFactory = function (...args) {
|
const wrappedFactory: WrappedModuleFactory = function (...args) {
|
||||||
// Restore the original factory in all the module factories objects. We want to make sure the original factory is restored properly, no matter what is the Webpack instance
|
// Restore the original factory
|
||||||
for (const wreq of allWebpackInstances) {
|
defineModulesFactoryGetter(id, wrappedFactory.$$vencordOriginal!, true);
|
||||||
define(wreq.m, id, { value: wrappedFactory.$$vencordOriginal });
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
let [module, exports, require] = args;
|
let [module, exports, require] = args;
|
||||||
|
|
Loading…
Reference in a new issue