Make factory wrapper a little more future proof
This commit is contained in:
parent
1a8a901e0a
commit
affd527bc1
|
@ -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] ?? ""}`, "");
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue