WebpackPatcher: Avoid patching wrong instances (like Stripe)
This commit is contained in:
parent
94d45780f9
commit
5ec564558e
4 changed files with 152 additions and 101 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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue