mirror of
https://github.com/Vendicated/Vencord.git
synced 2025-02-24 15:35:11 +00:00
Merge branch 'dev' into stickercopy
This commit is contained in:
commit
7a65dc569d
5 changed files with 205 additions and 122 deletions
|
@ -20,8 +20,7 @@ export async function loadLazyChunks() {
|
||||||
const invalidChunks = new Set<PropertyKey>();
|
const invalidChunks = new Set<PropertyKey>();
|
||||||
const deferredRequires = new Set<PropertyKey>();
|
const deferredRequires = new Set<PropertyKey>();
|
||||||
|
|
||||||
let chunksSearchingResolve: (value: void) => void;
|
const { promise: chunksSearchingDone, resolve: chunksSearchingResolve } = Promise.withResolvers<void>();
|
||||||
const chunksSearchingDone = new Promise<void>(r => chunksSearchingResolve = r);
|
|
||||||
|
|
||||||
// True if resolved, false otherwise
|
// True if resolved, false otherwise
|
||||||
const chunksSearchPromises = [] as Array<() => boolean>;
|
const chunksSearchPromises = [] as Array<() => boolean>;
|
||||||
|
|
|
@ -17,8 +17,7 @@ async function runReporter() {
|
||||||
try {
|
try {
|
||||||
ReporterLogger.log("Starting test...");
|
ReporterLogger.log("Starting test...");
|
||||||
|
|
||||||
let loadLazyChunksResolve: (value: void) => void;
|
const { promise: loadLazyChunksDone, resolve: loadLazyChunksResolve } = Promise.withResolvers<void>();
|
||||||
const loadLazyChunksDone = new Promise<void>(r => loadLazyChunksResolve = r);
|
|
||||||
|
|
||||||
// The main patch for starting the reporter chunk loading
|
// The main patch for starting the reporter chunk loading
|
||||||
addPatch({
|
addPatch({
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { AnyModuleFactory, AnyWebpackRequire, MaybePatchedModuleFactory, ModuleE
|
||||||
|
|
||||||
export const patches = [] as Patch[];
|
export const patches = [] as Patch[];
|
||||||
|
|
||||||
|
export const SYM_IS_PROXIED_FACTORY = Symbol("WebpackPatcher.isProxiedFactory");
|
||||||
export const SYM_ORIGINAL_FACTORY = Symbol("WebpackPatcher.originalFactory");
|
export const SYM_ORIGINAL_FACTORY = Symbol("WebpackPatcher.originalFactory");
|
||||||
export const SYM_PATCHED_SOURCE = Symbol("WebpackPatcher.patchedSource");
|
export const SYM_PATCHED_SOURCE = Symbol("WebpackPatcher.patchedSource");
|
||||||
export const SYM_PATCHED_BY = Symbol("WebpackPatcher.patchedBy");
|
export const SYM_PATCHED_BY = Symbol("WebpackPatcher.patchedBy");
|
||||||
|
@ -79,7 +80,8 @@ const define: typeof Reflect.defineProperty = (target, p, attributes) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// wreq.m is the Webpack object containing module factories. It is pre-populated with factories, and is also populated via webpackGlobal.push
|
// wreq.m is the Webpack object containing module factories. It is pre-populated with factories, and is also populated via webpackGlobal.push
|
||||||
// We use this setter to intercept when wreq.m is defined and apply patching to its factories.
|
// We use this setter to intercept when wreq.m is defined and setup our setters which decide whether we should patch these module factories
|
||||||
|
// and the Webpack instance where they are being defined.
|
||||||
|
|
||||||
// Factories can be patched in two ways. Eagerly or lazily.
|
// Factories can be patched in two ways. Eagerly or lazily.
|
||||||
// If we are patching eagerly, pre-populated factories are patched immediately and new factories are patched when set.
|
// If we are patching eagerly, pre-populated factories are patched immediately and new factories are patched when set.
|
||||||
|
@ -97,7 +99,7 @@ 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 Webpack instances.
|
// Ensure this is likely one of Discord main Webpack instances.
|
||||||
// We may catch Discord bundled libs, React Devtools or other extensions Webpack instances 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:{}")) {
|
||||||
|
@ -105,53 +107,90 @@ define(Function.prototype, "m", {
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1];
|
const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1];
|
||||||
logger.info("Found Webpack module factories" + interpolateIfDefined` in ${fileName}`);
|
|
||||||
|
|
||||||
allWebpackInstances.add(this);
|
// Define a setter for the bundlePath property of WebpackRequire. Only Webpack instances which include chunk loading functionality,
|
||||||
|
// like the main Discord Webpack, have this property.
|
||||||
// Define a setter for the ensureChunk property of WebpackRequire. Only the main Webpack (which is the only that includes chunk loading) has this property.
|
// So if the setter is called with the Discord bundlePath, this means we should patch this instance and initialize the internal references to WebpackRequire.
|
||||||
// So if the setter is called, this means we can initialize the internal references to WebpackRequire.
|
define(this, "p", {
|
||||||
define(this, "e", {
|
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
|
|
||||||
set(this: WebpackRequire, ensureChunk: WebpackRequire["e"]) {
|
set(this: AnyWebpackRequire, bundlePath: NonNullable<AnyWebpackRequire["p"]>) {
|
||||||
define(this, "e", { value: ensureChunk });
|
define(this, "p", { value: bundlePath });
|
||||||
clearTimeout(setterTimeout);
|
clearTimeout(bundlePathTimeout);
|
||||||
|
|
||||||
logger.info("Main WebpackInstance found" + interpolateIfDefined` in ${fileName}` + ", initializing internal references to WebpackRequire");
|
if (bundlePath !== "/assets/") {
|
||||||
_initWebpack(this);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
patchThisInstance();
|
||||||
|
|
||||||
|
if (wreq == null && this.c != null) {
|
||||||
|
logger.info("Main WebpackInstance found" + interpolateIfDefined` in ${fileName}` + ", initializing internal references to WebpackRequire");
|
||||||
|
_initWebpack(this as WebpackRequire);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// setImmediate to clear this property setter if this is not the main Webpack.
|
|
||||||
// If this is the main Webpack, wreq.e will always be set before the timeout runs.
|
|
||||||
const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "e"), 0);
|
|
||||||
|
|
||||||
// Patch the pre-populated factories
|
// In the past, the sentry Webpack instance which we also wanted to patch used to rely on chunks being loaded before initting sentry.
|
||||||
for (const moduleId in originalModules) {
|
// This Webpack instance did not include actual chunk loading, and only awaited for them to be loaded, which means it did not include the bundlePath property.
|
||||||
const originalFactory = originalModules[moduleId];
|
// To keep backwards compability, in case this is ever the case again, and keep patching this type of instance, we explicity patch instances which include wreq.O and not wreq.p.
|
||||||
|
// Since we cannot check what is the bundlePath of the instance to filter for the Discord bundlePath, we only patch it if wreq.p is not included,
|
||||||
|
// which means the instance relies on another instance which does chunk loading, and that makes it very likely to only target Discord Webpack instances like the old sentry.
|
||||||
|
|
||||||
if (updateExistingFactory(originalModules, moduleId, originalFactory, originalModules, true)) {
|
// Instead of patching when wreq.O is defined, wait for when wreq.O.j is defined, since that will be one of the last things to happen,
|
||||||
continue;
|
// which can assure wreq.p could have already been defined before.
|
||||||
|
define(this, "O", {
|
||||||
|
enumerable: false,
|
||||||
|
|
||||||
|
set(this: AnyWebpackRequire, onChunksLoaded: NonNullable<AnyWebpackRequire["O"]>) {
|
||||||
|
define(this, "O", { value: onChunksLoaded });
|
||||||
|
clearTimeout(onChunksLoadedTimeout);
|
||||||
|
|
||||||
|
const wreq = this;
|
||||||
|
define(onChunksLoaded, "j", {
|
||||||
|
enumerable: false,
|
||||||
|
|
||||||
|
set(this: NonNullable<AnyWebpackRequire["O"]>, j: NonNullable<AnyWebpackRequire["O"]>["j"]) {
|
||||||
|
define(this, "j", { value: j });
|
||||||
|
|
||||||
|
if (wreq.p == null) {
|
||||||
|
patchThisInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyFactoryListeners(moduleId, originalFactory);
|
|
||||||
|
|
||||||
const proxiedFactory = new Proxy(Settings.eagerPatches ? patchFactory(moduleId, originalFactory) : originalFactory, moduleFactoryHandler);
|
|
||||||
define(originalModules, moduleId, { value: proxiedFactory });
|
|
||||||
}
|
|
||||||
|
|
||||||
define(originalModules, Symbol.toStringTag, {
|
|
||||||
value: "ModuleFactories",
|
|
||||||
enumerable: false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const proxiedModuleFactories = new Proxy(originalModules, moduleFactoriesHandler);
|
// If neither of these properties setters were triggered, delete them as they are not needed anymore.
|
||||||
/*
|
const bundlePathTimeout = setTimeout(() => Reflect.deleteProperty(this, "p"), 0);
|
||||||
If Webpack ever decides to set module factories using the variable of the modules object directly, instead of wreq.m, switch the proxy to the prototype
|
const onChunksLoadedTimeout = setTimeout(() => Reflect.deleteProperty(this, "O"), 0);
|
||||||
Reflect.setPrototypeOf(originalModules, new Proxy(originalModules, moduleFactoriesHandler));
|
|
||||||
*/
|
|
||||||
|
|
||||||
define(this, "m", { value: proxiedModuleFactories });
|
/**
|
||||||
|
* Patch the current Webpack instance assigned to `this` context.
|
||||||
|
* This should only be called if this instance was later found to be one we need to patch.
|
||||||
|
*/
|
||||||
|
const patchThisInstance = () => {
|
||||||
|
logger.info("Found Webpack module factories" + interpolateIfDefined` in ${fileName}`);
|
||||||
|
allWebpackInstances.add(this);
|
||||||
|
|
||||||
|
// Proxy (and maybe patch) pre-populated factories
|
||||||
|
for (const moduleId in originalModules) {
|
||||||
|
updateExistingOrProxyFactory(originalModules, moduleId, originalModules[moduleId], originalModules, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
define(originalModules, Symbol.toStringTag, {
|
||||||
|
value: "ModuleFactories",
|
||||||
|
enumerable: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const proxiedModuleFactories = new Proxy(originalModules, moduleFactoriesHandler);
|
||||||
|
/*
|
||||||
|
If Webpack 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(originalModules, new Proxy(originalModules, moduleFactoriesHandler));
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(this, "m", { value: proxiedModuleFactories });
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -172,93 +211,102 @@ const moduleFactoriesHandler: ProxyHandler<AnyWebpackRequire["m"]> = {
|
||||||
},
|
},
|
||||||
*/
|
*/
|
||||||
|
|
||||||
set(target, p, newValue, receiver) {
|
set: updateExistingOrProxyFactory
|
||||||
if (updateExistingFactory(target, p, newValue, receiver)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyFactoryListeners(p, newValue);
|
|
||||||
|
|
||||||
const proxiedFactory = new Proxy(Settings.eagerPatches ? patchFactory(p, newValue) : newValue, moduleFactoryHandler);
|
|
||||||
return Reflect.set(target, p, proxiedFactory, receiver);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// The proxy for patching lazily and/or running factories with our wrapper.
|
// The proxy for patching lazily and/or running factories with our wrapper.
|
||||||
const moduleFactoryHandler: ProxyHandler<MaybePatchedModuleFactory> = {
|
const moduleFactoryHandler: ProxyHandler<MaybePatchedModuleFactory> = {
|
||||||
apply(target, thisArg: unknown, argArray: Parameters<AnyModuleFactory>) {
|
apply(target, thisArg: unknown, argArray: Parameters<AnyModuleFactory>) {
|
||||||
// SAFETY: Factories have `name` as their key in the module factories object, and that is always their module id
|
|
||||||
const moduleId = target.name;
|
|
||||||
|
|
||||||
// SYM_ORIGINAL_FACTORY means the factory has already been patched
|
// SYM_ORIGINAL_FACTORY means the factory has already been patched
|
||||||
if (target[SYM_ORIGINAL_FACTORY] != null) {
|
if (target[SYM_ORIGINAL_FACTORY] != null) {
|
||||||
return runFactoryWithWrap(moduleId, target as PatchedModuleFactory, thisArg, argArray);
|
return runFactoryWithWrap(target as PatchedModuleFactory, thisArg, argArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: Factories have `name` as their key in the module factories object, and that is always their module id
|
||||||
|
const moduleId: string = target.name;
|
||||||
|
|
||||||
const patchedFactory = patchFactory(moduleId, target);
|
const patchedFactory = patchFactory(moduleId, target);
|
||||||
return runFactoryWithWrap(moduleId, patchedFactory, thisArg, argArray);
|
return runFactoryWithWrap(patchedFactory, thisArg, argArray);
|
||||||
},
|
},
|
||||||
|
|
||||||
get(target, p, receiver) {
|
get(target, p, receiver) {
|
||||||
if (target[SYM_ORIGINAL_FACTORY] != null && (p === SYM_PATCHED_SOURCE || p === SYM_PATCHED_BY)) {
|
if (p === SYM_IS_PROXIED_FACTORY) {
|
||||||
return Reflect.get(target[SYM_ORIGINAL_FACTORY], p, target[SYM_ORIGINAL_FACTORY]);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const v = Reflect.get(target, p, receiver);
|
const originalFactory: AnyModuleFactory = target[SYM_ORIGINAL_FACTORY] ?? target;
|
||||||
|
|
||||||
// Make proxied factories `toString` return their original factory `toString`
|
// Redirect these properties to the original factory, including making `toString` return the original factory `toString`
|
||||||
if (p === "toString") {
|
if (p === "toString" || p === SYM_PATCHED_SOURCE || p === SYM_PATCHED_BY) {
|
||||||
return v.bind(target[SYM_ORIGINAL_FACTORY] ?? target);
|
const v = Reflect.get(originalFactory, p, originalFactory);
|
||||||
|
return p === "toString" ? v.bind(originalFactory) : v;
|
||||||
}
|
}
|
||||||
|
|
||||||
return v;
|
return Reflect.get(target, p, receiver);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function updateExistingOrProxyFactory(moduleFactories: AnyWebpackRequire["m"], moduleId: PropertyKey, newFactory: AnyModuleFactory, receiver: any, ignoreExistingInTarget = false) {
|
||||||
|
if (updateExistingFactory(moduleFactories, moduleId, newFactory, receiver, ignoreExistingInTarget)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyFactoryListeners(moduleId, newFactory);
|
||||||
|
|
||||||
|
const proxiedFactory = new Proxy(Settings.eagerPatches ? patchFactory(moduleId, newFactory) : newFactory, moduleFactoryHandler);
|
||||||
|
return Reflect.set(moduleFactories, moduleId, proxiedFactory, receiver);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a factory that exists in any Webpack instance with a new original factory.
|
* Update a duplicated factory that exists in any of the Webpack instances we track with a new original factory.
|
||||||
*
|
*
|
||||||
* @param moduleFactoriesTarget The module factories where this new original factory is being set
|
* @param moduleFactories The module factories where this new original factory is being set
|
||||||
* @param moduleId The id of the module
|
* @param moduleId The id of the module
|
||||||
* @param newFactory The new original factory
|
* @param newFactory The new original factory
|
||||||
* @param receiver The receiver of the new factory
|
* @param receiver The receiver of the factory
|
||||||
* @param ignoreExistingInTarget Whether to ignore checking if the factory already exists in the moduleFactoriesTarget
|
* @param ignoreExistingInTarget Whether to ignore checking if the factory already exists in the moduleFactories where it is being set
|
||||||
* @returns Whether the original factory was updated, or false if it doesn't exist in any Webpack instance
|
* @returns Whether the original factory was updated, or false if it doesn't exist in any of the tracked Webpack instances
|
||||||
*/
|
*/
|
||||||
function updateExistingFactory(moduleFactoriesTarget: AnyWebpackRequire["m"], moduleId: PropertyKey, newFactory: AnyModuleFactory, receiver: any, ignoreExistingInTarget: boolean = false) {
|
function updateExistingFactory(moduleFactories: AnyWebpackRequire["m"], moduleId: PropertyKey, newFactory: AnyModuleFactory, receiver: any, ignoreExistingInTarget) {
|
||||||
let existingFactory: TypedPropertyDescriptor<AnyModuleFactory> | undefined;
|
let existingFactory: AnyModuleFactory | undefined;
|
||||||
let moduleFactoriesWithFactory: AnyWebpackRequire["m"] | undefined;
|
let moduleFactoriesWithFactory: AnyWebpackRequire["m"] | undefined;
|
||||||
for (const wreq of allWebpackInstances) {
|
for (const wreq of allWebpackInstances) {
|
||||||
if (ignoreExistingInTarget && wreq.m === moduleFactoriesTarget) {
|
if (ignoreExistingInTarget && wreq.m === moduleFactories) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Object.hasOwn(wreq.m, moduleId)) {
|
if (Object.hasOwn(wreq.m, moduleId)) {
|
||||||
existingFactory = Reflect.getOwnPropertyDescriptor(wreq.m, moduleId);
|
existingFactory = wreq.m[moduleId];
|
||||||
moduleFactoriesWithFactory = wreq.m;
|
moduleFactoriesWithFactory = wreq.m;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingFactory != null) {
|
if (existingFactory != null) {
|
||||||
// If existingFactory exists in any Webpack instance, it's either wrapped in our proxy, or it has already been required.
|
// Sanity check to make sure these factories are equal
|
||||||
// In the case it is wrapped in our proxy, we need the Webpack instance with this new original factory to also have our proxy.
|
if (String(newFactory) !== String(existingFactory)) {
|
||||||
// So, define the descriptor of the existing factory on it.
|
return false;
|
||||||
if (moduleFactoriesWithFactory !== moduleFactoriesTarget) {
|
|
||||||
Reflect.defineProperty(receiver, moduleId, existingFactory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingFactoryValue = moduleFactoriesWithFactory![moduleId];
|
// If existingFactory exists in any of the Webpack instances we track, it's either wrapped in our proxy, or it has already been required.
|
||||||
|
// In the case it is wrapped in our proxy, and the instance we are setting does not already have it, we need to make sure the instance contains our proxy too.
|
||||||
|
if (moduleFactoriesWithFactory !== moduleFactories && existingFactory[SYM_IS_PROXIED_FACTORY]) {
|
||||||
|
Reflect.set(moduleFactories, moduleId, existingFactory, receiver);
|
||||||
|
}
|
||||||
|
// Else, if it is not wrapped in our proxy, set this new original factory in all the instances
|
||||||
|
else {
|
||||||
|
defineInWebpackInstances(moduleId, newFactory);
|
||||||
|
}
|
||||||
|
|
||||||
// Update with the new original factory, if it does have a current original factory
|
// Update existingFactory with the new original, if it does have a current original factory
|
||||||
if (existingFactoryValue[SYM_ORIGINAL_FACTORY] != null) {
|
if (existingFactory[SYM_ORIGINAL_FACTORY] != null) {
|
||||||
existingFactoryValue[SYM_ORIGINAL_FACTORY] = newFactory;
|
existingFactory[SYM_ORIGINAL_FACTORY] = newFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Persist patched source and patched by in the new original factory
|
// Persist patched source and patched by in the new original factory
|
||||||
if (IS_DEV) {
|
if (IS_DEV) {
|
||||||
newFactory[SYM_PATCHED_SOURCE] = existingFactoryValue[SYM_PATCHED_SOURCE];
|
newFactory[SYM_PATCHED_SOURCE] = existingFactory[SYM_PATCHED_SOURCE];
|
||||||
newFactory[SYM_PATCHED_BY] = existingFactoryValue[SYM_PATCHED_BY];
|
newFactory[SYM_PATCHED_BY] = existingFactory[SYM_PATCHED_BY];
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -267,6 +315,18 @@ function updateExistingFactory(moduleFactoriesTarget: AnyWebpackRequire["m"], mo
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define a module factory in all the Webpack instances we track.
|
||||||
|
*
|
||||||
|
* @param moduleId The id of the module
|
||||||
|
* @param factory The factory
|
||||||
|
*/
|
||||||
|
function defineInWebpackInstances(moduleId: PropertyKey, factory: AnyModuleFactory) {
|
||||||
|
for (const wreq of allWebpackInstances) {
|
||||||
|
define(wreq.m, moduleId, { value: factory });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify all factory listeners.
|
* Notify all factory listeners.
|
||||||
*
|
*
|
||||||
|
@ -286,12 +346,11 @@ function notifyFactoryListeners(moduleId: PropertyKey, factory: AnyModuleFactory
|
||||||
/**
|
/**
|
||||||
* Run a (possibly) patched module factory with a wrapper which notifies our listeners.
|
* Run a (possibly) patched module factory with a wrapper which notifies our listeners.
|
||||||
*
|
*
|
||||||
* @param moduleId The id of the module
|
|
||||||
* @param patchedFactory The (possibly) patched module factory
|
* @param patchedFactory The (possibly) patched module factory
|
||||||
* @param thisArg The `value` of the call to the factory
|
* @param thisArg The `value` of the call to the factory
|
||||||
* @param argArray The arguments of the call to the factory
|
* @param argArray The arguments of the call to the factory
|
||||||
*/
|
*/
|
||||||
function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModuleFactory, thisArg: unknown, argArray: Parameters<MaybePatchedModuleFactory>) {
|
function runFactoryWithWrap(patchedFactory: PatchedModuleFactory, thisArg: unknown, argArray: Parameters<MaybePatchedModuleFactory>) {
|
||||||
const originalFactory = patchedFactory[SYM_ORIGINAL_FACTORY];
|
const originalFactory = patchedFactory[SYM_ORIGINAL_FACTORY];
|
||||||
|
|
||||||
if (patchedFactory === originalFactory) {
|
if (patchedFactory === originalFactory) {
|
||||||
|
@ -299,25 +358,23 @@ function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModule
|
||||||
delete patchedFactory[SYM_ORIGINAL_FACTORY];
|
delete patchedFactory[SYM_ORIGINAL_FACTORY];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore the original factory in all the module factories objects, discarding our proxy and allowing it to be garbage collected
|
|
||||||
for (const wreq of allWebpackInstances) {
|
|
||||||
define(wreq.m, moduleId, { value: originalFactory });
|
|
||||||
}
|
|
||||||
|
|
||||||
let [module, exports, require] = argArray;
|
let [module, exports, require] = argArray;
|
||||||
|
|
||||||
|
// Restore the original factory in all the module factories objects, discarding our proxy and allowing it to be garbage collected
|
||||||
|
defineInWebpackInstances(module.id, originalFactory);
|
||||||
|
|
||||||
if (wreq == null) {
|
if (wreq == null) {
|
||||||
if (!wreqFallbackApplied) {
|
if (!wreqFallbackApplied) {
|
||||||
wreqFallbackApplied = true;
|
wreqFallbackApplied = true;
|
||||||
|
|
||||||
// Make sure the require argument is actually the WebpackRequire function
|
// Make sure the require argument is actually the WebpackRequire function
|
||||||
if (typeof require === "function" && require.m != null) {
|
if (typeof require === "function" && require.m != null && require.c != null) {
|
||||||
const { stack } = new Error();
|
const { stack } = new Error();
|
||||||
const webpackInstanceFileName = stack?.match(/\/assets\/(.+?\.js)/)?.[1];
|
const webpackInstanceFileName = stack?.match(/\/assets\/(.+?\.js)/)?.[1];
|
||||||
|
|
||||||
logger.warn(
|
logger.warn(
|
||||||
"WebpackRequire was not initialized, falling back to WebpackRequire passed to the first called wrapped module factory (" +
|
"WebpackRequire was not initialized, falling back to WebpackRequire passed to the first called wrapped module factory (" +
|
||||||
`id: ${String(moduleId)}` + interpolateIfDefined`, WebpackInstance origin: ${webpackInstanceFileName}` +
|
`id: ${String(module.id)}` + interpolateIfDefined`, WebpackInstance origin: ${webpackInstanceFileName}` +
|
||||||
")"
|
")"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -355,8 +412,8 @@ function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModule
|
||||||
|
|
||||||
if (shouldIgnoreModule) {
|
if (shouldIgnoreModule) {
|
||||||
if (require.c != null) {
|
if (require.c != null) {
|
||||||
Object.defineProperty(require.c, moduleId, {
|
Object.defineProperty(require.c, module.id, {
|
||||||
value: require.c[moduleId],
|
value: require.c[module.id],
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
writable: true
|
writable: true
|
||||||
|
@ -369,7 +426,7 @@ function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModule
|
||||||
|
|
||||||
for (const callback of moduleListeners) {
|
for (const callback of moduleListeners) {
|
||||||
try {
|
try {
|
||||||
callback(exports, moduleId);
|
callback(exports, module.id);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error("Error in Webpack module listener:\n", err, callback);
|
logger.error("Error in Webpack module listener:\n", err, callback);
|
||||||
}
|
}
|
||||||
|
@ -379,7 +436,7 @@ function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModule
|
||||||
try {
|
try {
|
||||||
if (filter(exports)) {
|
if (filter(exports)) {
|
||||||
waitForSubscriptions.delete(filter);
|
waitForSubscriptions.delete(filter);
|
||||||
callback(exports, moduleId);
|
callback(exports, module.id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,7 +449,7 @@ function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModule
|
||||||
|
|
||||||
if (exportValue != null && filter(exportValue)) {
|
if (exportValue != null && filter(exportValue)) {
|
||||||
waitForSubscriptions.delete(filter);
|
waitForSubscriptions.delete(filter);
|
||||||
callback(exportValue, moduleId);
|
callback(exportValue, module.id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,10 +97,6 @@ export const moduleListeners = new Set<CallbackFn>();
|
||||||
export const factoryListeners = new Set<FactoryListernFn>();
|
export const factoryListeners = new Set<FactoryListernFn>();
|
||||||
|
|
||||||
export function _initWebpack(webpackRequire: WebpackRequire) {
|
export function _initWebpack(webpackRequire: WebpackRequire) {
|
||||||
if (webpackRequire.c == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wreq = webpackRequire;
|
wreq = webpackRequire;
|
||||||
cache = webpackRequire.c;
|
cache = webpackRequire.c;
|
||||||
|
|
||||||
|
|
74
src/webpack/wreq.d.ts
vendored
74
src/webpack/wreq.d.ts
vendored
|
@ -33,27 +33,45 @@ export type AsyncModuleBody = (
|
||||||
asyncResult: (error?: any) => void
|
asyncResult: (error?: any) => void
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
|
|
||||||
export type ChunkHandlers = {
|
export type EnsureChunkHandlers = {
|
||||||
/**
|
/**
|
||||||
* Ensures the js file for this chunk is loaded, or starts to load if it's not.
|
* Ensures the js file for this chunk is loaded, or starts to load if it's not.
|
||||||
* @param chunkId The chunk id
|
* @param chunkId The chunk id
|
||||||
* @param promises The promises array to add the loading promise to
|
* @param promises The promises array to add the loading promise to
|
||||||
*/
|
*/
|
||||||
j: (this: ChunkHandlers, chunkId: PropertyKey, promises: Promise<void[]>) => void,
|
j: (this: EnsureChunkHandlers, chunkId: PropertyKey, promises: Promise<void[]>) => void;
|
||||||
/**
|
/**
|
||||||
* Ensures the css file for this chunk is loaded, or starts to load if it's not.
|
* Ensures the css file for this chunk is loaded, or starts to load if it's not.
|
||||||
* @param chunkId The chunk id
|
* @param chunkId The chunk id
|
||||||
* @param promises The promises array to add the loading promise to. This array will likely contain the promise of the js file too
|
* @param promises The promises array to add the loading promise to. This array will likely contain the promise of the js file too
|
||||||
*/
|
*/
|
||||||
css: (this: ChunkHandlers, chunkId: PropertyKey, promises: Promise<void[]>) => void,
|
css: (this: EnsureChunkHandlers, chunkId: PropertyKey, promises: Promise<void[]>) => void;
|
||||||
|
/**
|
||||||
|
* Trigger for prefetching next chunks. This is called after ensuring a chunk is loaded and internally looks up
|
||||||
|
* a map to see if the chunk that just loaded has next chunks to prefetch.
|
||||||
|
*
|
||||||
|
* Note that this does not add an extra promise to the promises array, and instead only executes the prefetching after
|
||||||
|
* calling Promise.all on the promises array.
|
||||||
|
* @param chunkId The chunk id
|
||||||
|
* @param promises The promises array of ensuring the chunk is loaded
|
||||||
|
*/
|
||||||
|
prefetch: (this: EnsureChunkHandlers, chunkId: PropertyKey, promises: Promise<void[]>) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PrefetchChunkHandlers = {
|
||||||
|
/**
|
||||||
|
* Prefetches the js file for this chunk.
|
||||||
|
* @param chunkId The chunk id
|
||||||
|
*/
|
||||||
|
j: (this: PrefetchChunkHandlers, chunkId: PropertyKey) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ScriptLoadDone = (event: Event) => void;
|
export type ScriptLoadDone = (event: Event) => void;
|
||||||
|
|
||||||
// export type OnChunksLoaded = ((this: WebpackRequire, result: any, chunkIds: PropertyKey[] | undefined | null, callback: () => any, priority: number) => any) & {
|
export type OnChunksLoaded = ((this: WebpackRequire, result: any, chunkIds: PropertyKey[] | undefined | null, callback: () => any, priority: number) => any) & {
|
||||||
// /** Check if a chunk has been loaded */
|
/** Check if a chunk has been loaded */
|
||||||
// j: (this: OnChunksLoaded, chunkId: PropertyKey) => boolean;
|
j: (this: OnChunksLoaded, chunkId: PropertyKey) => boolean;
|
||||||
// };
|
};
|
||||||
|
|
||||||
export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
|
export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
|
||||||
/** The module factories, where all modules that have been loaded are stored (pre-loaded or loaded by lazy chunks) */
|
/** The module factories, where all modules that have been loaded are stored (pre-loaded or loaded by lazy chunks) */
|
||||||
|
@ -135,13 +153,20 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
|
||||||
* // exports is now { exportName: someExportedValue } (but each value is actually a getter)
|
* // exports is now { exportName: someExportedValue } (but each value is actually a getter)
|
||||||
*/
|
*/
|
||||||
d: (this: WebpackRequire, exports: AnyRecord, definiton: AnyRecord) => void;
|
d: (this: WebpackRequire, exports: AnyRecord, definiton: AnyRecord) => void;
|
||||||
/** The chunk handlers, which are used to ensure the files of the chunks are loaded, or load if necessary */
|
/** The ensure chunk handlers, which are used to ensure the files of the chunks are loaded, or load if necessary */
|
||||||
f: ChunkHandlers;
|
f: EnsureChunkHandlers;
|
||||||
/**
|
/**
|
||||||
* The ensure chunk function, it ensures a chunk is loaded, or loads if needed.
|
* The ensure chunk function, it ensures a chunk is loaded, or loads if needed.
|
||||||
* Internally it uses the handlers in {@link WebpackRequire.f} to load/ensure the chunk is loaded.
|
* Internally it uses the handlers in {@link WebpackRequire.f} to load/ensure the chunk is loaded.
|
||||||
*/
|
*/
|
||||||
e: (this: WebpackRequire, chunkId: PropertyKey) => Promise<void[]>;
|
e: (this: WebpackRequire, chunkId: PropertyKey) => Promise<void[]>;
|
||||||
|
/** The prefetch chunk handlers, which are used to prefetch the files of the chunks */
|
||||||
|
F: PrefetchChunkHandlers;
|
||||||
|
/**
|
||||||
|
* The prefetch chunk function.
|
||||||
|
* Internally it uses the handlers in {@link WebpackRequire.F} to prefetch a chunk.
|
||||||
|
*/
|
||||||
|
E: (this: WebpackRequire, chunkId: PropertyKey) => void;
|
||||||
/** Get the filename for the css part of a chunk */
|
/** Get the filename for the css part of a chunk */
|
||||||
k: (this: WebpackRequire, chunkId: PropertyKey) => string;
|
k: (this: WebpackRequire, chunkId: PropertyKey) => string;
|
||||||
/** Get the filename for the js part of a chunk */
|
/** Get the filename for the js part of a chunk */
|
||||||
|
@ -162,18 +187,18 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
|
||||||
r: (this: WebpackRequire, exports: ModuleExports) => void;
|
r: (this: WebpackRequire, exports: ModuleExports) => void;
|
||||||
/** Node.js module decorator. Decorates a module as a Node.js module */
|
/** Node.js module decorator. Decorates a module as a Node.js module */
|
||||||
nmd: (this: WebpackRequire, module: Module) => any;
|
nmd: (this: WebpackRequire, module: Module) => any;
|
||||||
// /**
|
/**
|
||||||
// * Register deferred code which will be executed when the passed chunks are loaded.
|
* Register deferred code which will be executed when the passed chunks are loaded.
|
||||||
// *
|
*
|
||||||
// * If chunkIds is defined, it defers the execution of the callback and returns undefined.
|
* If chunkIds is defined, it defers the execution of the callback and returns undefined.
|
||||||
// *
|
*
|
||||||
// * If chunkIds is undefined, and no deferred code exists or can be executed, it returns the value of the result argument.
|
* If chunkIds is undefined, and no deferred code exists or can be executed, it returns the value of the result argument.
|
||||||
// *
|
*
|
||||||
// * If chunkIds is undefined, and some deferred code can already be executed, it returns the result of the callback function of the last deferred code.
|
* If chunkIds is undefined, and some deferred code can already be executed, it returns the result of the callback function of the last deferred code.
|
||||||
// *
|
*
|
||||||
// * When (priority & 1) it will wait for all other handlers with lower priority to be executed before itself is executed.
|
* When (priority & 1) it will wait for all other handlers with lower priority to be executed before itself is executed.
|
||||||
// */
|
*/
|
||||||
// O: OnChunksLoaded;
|
O: OnChunksLoaded;
|
||||||
/**
|
/**
|
||||||
* Instantiate a wasm instance with source using "wasmModuleHash", and importObject "importsObj", and then assign the exports of its instance to "exports".
|
* Instantiate a wasm instance with source using "wasmModuleHash", and importObject "importsObj", and then assign the exports of its instance to "exports".
|
||||||
* @returns The exports argument, but now assigned with the exports of the wasm instance
|
* @returns The exports argument, but now assigned with the exports of the wasm instance
|
||||||
|
@ -185,6 +210,13 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
|
||||||
j: string;
|
j: string;
|
||||||
/** Document baseURI or WebWorker location.href */
|
/** Document baseURI or WebWorker location.href */
|
||||||
b: string;
|
b: string;
|
||||||
|
|
||||||
|
/* rspack only */
|
||||||
|
|
||||||
|
/** rspack version */
|
||||||
|
rv: (this: WebpackRequire) => string;
|
||||||
|
/** rspack unique id */
|
||||||
|
ruid: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Utility section for Vencord
|
// Utility section for Vencord
|
||||||
|
|
Loading…
Add table
Reference in a new issue