boops
This commit is contained in:
parent
bc367b1d2a
commit
40a1f48267
|
@ -18,3 +18,4 @@
|
||||||
|
|
||||||
export * as Common from "./common";
|
export * as Common from "./common";
|
||||||
export * from "./webpack";
|
export * from "./webpack";
|
||||||
|
export * from "./wreq.d";
|
||||||
|
|
|
@ -8,20 +8,19 @@ import { Logger } from "@utils/Logger";
|
||||||
import { UNCONFIGURABLE_PROPERTIES } from "@utils/misc";
|
import { UNCONFIGURABLE_PROPERTIES } from "@utils/misc";
|
||||||
import { canonicalizeMatch, canonicalizeReplacement } from "@utils/patches";
|
import { canonicalizeMatch, canonicalizeReplacement } from "@utils/patches";
|
||||||
import { PatchReplacement } from "@utils/types";
|
import { PatchReplacement } from "@utils/types";
|
||||||
import { WebpackInstance } from "discord-types/other";
|
|
||||||
|
|
||||||
import { traceFunction } from "../debug/Tracer";
|
import { traceFunction } from "../debug/Tracer";
|
||||||
import { patches } from "../plugins";
|
import { patches } from "../plugins";
|
||||||
import { _initWebpack, beforeInitListeners, factoryListeners, moduleListeners, subscriptions, wreq } from ".";
|
import { _initWebpack, beforeInitListeners, factoryListeners, ModuleFactory, moduleListeners, subscriptions, WebpackRequire, wreq } from ".";
|
||||||
|
|
||||||
const logger = new Logger("WebpackInterceptor", "#8caaee");
|
const logger = new Logger("WebpackInterceptor", "#8caaee");
|
||||||
const initCallbackRegex = canonicalizeMatch(/{return \i\(".+?"\)}/);
|
const initCallbackRegex = canonicalizeMatch(/{return \i\(".+?"\)}/);
|
||||||
|
|
||||||
const modulesProxyhandler: ProxyHandler<any> = {
|
const modulesProxyhandler: ProxyHandler<WebpackRequire["m"]> = {
|
||||||
...Object.fromEntries(Object.getOwnPropertyNames(Reflect).map(propName =>
|
...Object.fromEntries(Object.getOwnPropertyNames(Reflect).map(propName =>
|
||||||
[propName, (target: any, ...args: any[]) => Reflect[propName](target, ...args)]
|
[propName, (target: WebpackRequire["m"], ...args: any[]) => Reflect[propName](target, ...args)]
|
||||||
)),
|
)),
|
||||||
get: (target, p: string) => {
|
get: (target, p) => {
|
||||||
const mod = Reflect.get(target, p);
|
const mod = Reflect.get(target, p);
|
||||||
|
|
||||||
// If the property is not a module id, return the value of it without trying to patch
|
// If the property is not a module id, return the value of it without trying to patch
|
||||||
|
@ -48,7 +47,7 @@ const modulesProxyhandler: ProxyHandler<any> = {
|
||||||
Object.defineProperty(Function.prototype, "O", {
|
Object.defineProperty(Function.prototype, "O", {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
|
|
||||||
set(onChunksLoaded: any) {
|
set(onChunksLoaded: WebpackRequire["O"]) {
|
||||||
// When using react devtools or other extensions, or even when discord loads the sentry, we may also catch their webpack here.
|
// When using react devtools or other extensions, or even when discord loads the sentry, we may also catch their webpack here.
|
||||||
// This ensures we actually got the right one
|
// This ensures we actually got the right one
|
||||||
// this.e (wreq.e) is the method for loading a chunk, and only the main webpack has it
|
// this.e (wreq.e) is the method for loading a chunk, and only the main webpack has it
|
||||||
|
@ -59,14 +58,14 @@ Object.defineProperty(Function.prototype, "O", {
|
||||||
delete (Function.prototype as any).O;
|
delete (Function.prototype as any).O;
|
||||||
|
|
||||||
const originalOnChunksLoaded = onChunksLoaded;
|
const originalOnChunksLoaded = onChunksLoaded;
|
||||||
onChunksLoaded = function (this: unknown, result: any, chunkIds: string[], callback: () => any, priority: number) {
|
onChunksLoaded = function (result, chunkIds, callback, priority) {
|
||||||
if (callback != null && initCallbackRegex.test(callback.toString())) {
|
if (callback != null && initCallbackRegex.test(callback.toString())) {
|
||||||
Object.defineProperty(this, "O", {
|
Object.defineProperty(this, "O", {
|
||||||
value: originalOnChunksLoaded,
|
value: originalOnChunksLoaded,
|
||||||
configurable: true
|
configurable: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const wreq = this as WebpackInstance;
|
const wreq = this;
|
||||||
|
|
||||||
const originalCallback = callback;
|
const originalCallback = callback;
|
||||||
callback = function (this: unknown) {
|
callback = function (this: unknown) {
|
||||||
|
@ -85,7 +84,7 @@ Object.defineProperty(Function.prototype, "O", {
|
||||||
}
|
}
|
||||||
|
|
||||||
originalOnChunksLoaded.apply(this, arguments as any);
|
originalOnChunksLoaded.apply(this, arguments as any);
|
||||||
};
|
} as WebpackRequire["O"];
|
||||||
|
|
||||||
onChunksLoaded.toString = originalOnChunksLoaded.toString.bind(originalOnChunksLoaded);
|
onChunksLoaded.toString = originalOnChunksLoaded.toString.bind(originalOnChunksLoaded);
|
||||||
}
|
}
|
||||||
|
@ -131,7 +130,7 @@ Object.defineProperty(Function.prototype, "m", {
|
||||||
|
|
||||||
let webpackNotInitializedLogged = false;
|
let webpackNotInitializedLogged = false;
|
||||||
|
|
||||||
function patchFactory(id: string, mod: (module: any, exports: any, require: WebpackInstance) => void) {
|
function patchFactory(id: string | number, mod: ModuleFactory) {
|
||||||
for (const factoryListener of factoryListeners) {
|
for (const factoryListener of factoryListeners) {
|
||||||
try {
|
try {
|
||||||
factoryListener(mod);
|
factoryListener(mod);
|
||||||
|
@ -255,7 +254,7 @@ function patchFactory(id: string, mod: (module: any, exports: any, require: Webp
|
||||||
if (!patch.all) patches.splice(i--, 1);
|
if (!patch.all) patches.splice(i--, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function patchedFactory(module: any, exports: any, require: WebpackInstance) {
|
const patchedFactory: ModuleFactory = (module, exports, require) => {
|
||||||
if (wreq == null && IS_DEV) {
|
if (wreq == null && IS_DEV) {
|
||||||
if (!webpackNotInitializedLogged) {
|
if (!webpackNotInitializedLogged) {
|
||||||
webpackNotInitializedLogged = true;
|
webpackNotInitializedLogged = true;
|
||||||
|
@ -312,7 +311,7 @@ function patchFactory(id: string, mod: (module: any, exports: any, require: Webp
|
||||||
logger.error("Error while firing callback for Webpack subscription:\n", err, filter, callback);
|
logger.error("Error while firing callback for Webpack subscription:\n", err, filter, callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
patchedFactory.toString = originalMod.toString.bind(originalMod);
|
patchedFactory.toString = originalMod.toString.bind(originalMod);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
|
@ -20,9 +20,9 @@ import { makeLazy, proxyLazy } from "@utils/lazy";
|
||||||
import { LazyComponent } from "@utils/lazyReact";
|
import { LazyComponent } from "@utils/lazyReact";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { canonicalizeMatch } from "@utils/patches";
|
import { canonicalizeMatch } from "@utils/patches";
|
||||||
import type { WebpackInstance } from "discord-types/other";
|
|
||||||
|
|
||||||
import { traceFunction } from "../debug/Tracer";
|
import { traceFunction } from "../debug/Tracer";
|
||||||
|
import { ModuleExports, ModuleFactory, WebpackRequire } from "./wreq";
|
||||||
|
|
||||||
const logger = new Logger("Webpack");
|
const logger = new Logger("Webpack");
|
||||||
|
|
||||||
|
@ -33,8 +33,8 @@ export let _resolveReady: () => void;
|
||||||
*/
|
*/
|
||||||
export const onceReady = new Promise<void>(r => _resolveReady = r);
|
export const onceReady = new Promise<void>(r => _resolveReady = r);
|
||||||
|
|
||||||
export let wreq: WebpackInstance;
|
export let wreq: WebpackRequire;
|
||||||
export let cache: WebpackInstance["c"];
|
export let cache: WebpackRequire["c"];
|
||||||
|
|
||||||
export type FilterFn = (mod: any) => boolean;
|
export type FilterFn = (mod: any) => boolean;
|
||||||
|
|
||||||
|
@ -68,14 +68,14 @@ export const filters = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CallbackFn = (mod: any, id: string) => void;
|
export type CallbackFn = (module: ModuleExports, id: PropertyKey) => void;
|
||||||
|
|
||||||
export const subscriptions = new Map<FilterFn, CallbackFn>();
|
export const subscriptions = new Map<FilterFn, CallbackFn>();
|
||||||
export const moduleListeners = new Set<CallbackFn>();
|
export const moduleListeners = new Set<CallbackFn>();
|
||||||
export const factoryListeners = new Set<(factory: (module: any, exports: any, require: WebpackInstance) => void) => void>();
|
export const factoryListeners = new Set<(factory: ModuleFactory) => void>();
|
||||||
export const beforeInitListeners = new Set<(wreq: WebpackInstance) => void>();
|
export const beforeInitListeners = new Set<(wreq: WebpackRequire) => void>();
|
||||||
|
|
||||||
export function _initWebpack(webpackRequire: WebpackInstance) {
|
export function _initWebpack(webpackRequire: WebpackRequire) {
|
||||||
wreq = webpackRequire;
|
wreq = webpackRequire;
|
||||||
cache = webpackRequire.c;
|
cache = webpackRequire.c;
|
||||||
}
|
}
|
||||||
|
@ -500,6 +500,7 @@ export function search(...filters: Array<string | RegExp>) {
|
||||||
const factories = wreq.m;
|
const factories = wreq.m;
|
||||||
outer:
|
outer:
|
||||||
for (const id in factories) {
|
for (const id in factories) {
|
||||||
|
// @ts-ignore
|
||||||
const factory = factories[id].original ?? factories[id];
|
const factory = factories[id].original ?? factories[id];
|
||||||
const str: string = factory.toString();
|
const str: string = factory.toString();
|
||||||
for (const filter of filters) {
|
for (const filter of filters) {
|
||||||
|
|
54
src/webpack/wreq.d.ts
vendored
54
src/webpack/wreq.d.ts
vendored
|
@ -4,46 +4,46 @@
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type AnyRecord = Record<PropertyKey, any>;
|
export type AnyRecord = Record<PropertyKey, any>;
|
||||||
|
|
||||||
type ModuleExports = any;
|
export type ModuleExports = any;
|
||||||
|
|
||||||
type Module = {
|
export type Module = {
|
||||||
id: PropertyKey;
|
id: PropertyKey;
|
||||||
loaded: boolean;
|
loaded: boolean;
|
||||||
exports: ModuleExports;
|
exports: ModuleExports;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** exports ({@link ModuleExports}) can be anything, however initially it is always an empty object */
|
/** exports ({@link ModuleExports}) can be anything, however initially it is always an empty object */
|
||||||
type ModuleFactory = (module: Module, exports: AnyRecord, require: WebpackRequire) => void;
|
export type ModuleFactory = (module: Module, exports: AnyRecord, require: WebpackRequire) => void;
|
||||||
|
|
||||||
type AsyncModuleBody = (
|
export type AsyncModuleBody = (
|
||||||
handleDependencies: (deps: Promise<any>[]) => Promise<any[]> & (() => void)
|
handleDependencies: (deps: Promise<any>[]) => Promise<any[]> & (() => void)
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
|
|
||||||
type ChunkHandlers = {
|
export type ChunkHandlers = {
|
||||||
/**
|
/**
|
||||||
* 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: (chunkId: string | number, promises: Promise<void[]>) => void,
|
j: (this: ChunkHandlers, 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: (chunkId: string | number, promises: Promise<void[]>) => void,
|
css: (this: ChunkHandlers, chunkId: PropertyKey, promises: Promise<void[]>) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
type ScriptLoadDone = (event: Event) => void;
|
export type ScriptLoadDone = (event: Event) => void;
|
||||||
|
|
||||||
type OnChunksLoaded = ((result: any, chunkIds: (string | number)[] | undefined, 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: (chunkId: string | number) => boolean;
|
j: (chunkId: PropertyKey) => boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type WebpackRequire = ((moduleId: PropertyKey) => Module) & {
|
export type WebpackRequire = ((moduleId: PropertyKey) => Module) & {
|
||||||
/** 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) */
|
||||||
m: Record<PropertyKey, ModuleFactory>;
|
m: Record<PropertyKey, ModuleFactory>;
|
||||||
/** The module cache, where all modules which have been WebpackRequire'd are stored */
|
/** The module cache, where all modules which have been WebpackRequire'd are stored */
|
||||||
|
@ -52,12 +52,12 @@ type WebpackRequire = ((moduleId: PropertyKey) => Module) & {
|
||||||
* Export star. Sets properties of "fromObject" to "toObject" as getters that return the value from "fromObject", like this:
|
* Export star. Sets properties of "fromObject" to "toObject" as getters that return the value from "fromObject", like this:
|
||||||
* @example
|
* @example
|
||||||
* const fromObject = { a: 1 };
|
* const fromObject = { a: 1 };
|
||||||
* Object.defineProperty(to, "a", {
|
* Object.defineProperty(fromObject, "a", {
|
||||||
* get: () => fromObject.a
|
* get: () => fromObject["a"]
|
||||||
* });
|
* });
|
||||||
* @returns fromObject
|
* @returns fromObject
|
||||||
*/
|
*/
|
||||||
es: (fromObject: AnyRecord, toObject: AnyRecord) => AnyRecord;
|
es: (this: WebpackRequire, fromObject: AnyRecord, toObject: AnyRecord) => AnyRecord;
|
||||||
/**
|
/**
|
||||||
* Creates an async module. The body function must be a async function.
|
* Creates an async module. The body function must be a async function.
|
||||||
* "module.exports" will be decorated with an AsyncModulePromise.
|
* "module.exports" will be decorated with an AsyncModulePromise.
|
||||||
|
@ -65,9 +65,9 @@ type WebpackRequire = ((moduleId: PropertyKey) => Module) & {
|
||||||
* To handle async dependencies correctly do this inside the body: "([a, b, c] = await handleDependencies([a, b, c]));".
|
* To handle async dependencies correctly do this inside the body: "([a, b, c] = await handleDependencies([a, b, c]));".
|
||||||
* If "hasAwaitAfterDependencies" is truthy, "handleDependencies()" must be called at the end of the body function.
|
* If "hasAwaitAfterDependencies" is truthy, "handleDependencies()" must be called at the end of the body function.
|
||||||
*/
|
*/
|
||||||
a: (module: Module, body: AsyncModuleBody, hasAwaitAfterDependencies?: boolean) => void;
|
a: (this: WebpackRequire, module: Module, body: AsyncModuleBody, hasAwaitAfterDependencies?: boolean) => void;
|
||||||
/** getDefaultExport function for compatibility with non-harmony modules */
|
/** getDefaultExport function for compatibility with non-harmony modules */
|
||||||
n: (module: Module) => () => ModuleExports;
|
n: (this: WebpackRequire, module: Module) => () => ModuleExports;
|
||||||
/**
|
/**
|
||||||
* Create a fake namespace object, useful for faking an __esModule with a default export.
|
* Create a fake namespace object, useful for faking an __esModule with a default export.
|
||||||
*
|
*
|
||||||
|
@ -81,7 +81,7 @@ type WebpackRequire = ((moduleId: PropertyKey) => Module) & {
|
||||||
*
|
*
|
||||||
* mode & (8|1): Behave like require
|
* mode & (8|1): Behave like require
|
||||||
*/
|
*/
|
||||||
t: (value: any, mode: number) => any;
|
t: (this: WebpackRequire, value: any, mode: number) => any;
|
||||||
/**
|
/**
|
||||||
* Define property getters. For every prop in "definiton", set a getter in "exports" for the value in "definitiion", like this:
|
* Define property getters. For every prop in "definiton", set a getter in "exports" for the value in "definitiion", like this:
|
||||||
* @example
|
* @example
|
||||||
|
@ -91,22 +91,22 @@ type WebpackRequire = ((moduleId: PropertyKey) => Module) & {
|
||||||
* Object.defineProperty(exports, key, { get: definition[key] }
|
* Object.defineProperty(exports, key, { get: definition[key] }
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
d: (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 chunk handlers, which are used to ensure the files of the chunks are loaded, or load if necessary */
|
||||||
f: ChunkHandlers;
|
f: ChunkHandlers;
|
||||||
/**
|
/**
|
||||||
* 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: (chunkId: string | number) => Promise<void[]>;
|
e: (this: WebpackRequire, chunkId: PropertyKey) => Promise<void[]>;
|
||||||
/** Get the filename name for the css part of a chunk */
|
/** Get the filename name for the css part of a chunk */
|
||||||
k: (chunkId: string | number) => `${chunkId}.css`;
|
k: (this: WebpackRequire, chunkId: PropertyKey) => `${chunkId}.css`;
|
||||||
/** Get the filename for the js part of a chunk */
|
/** Get the filename for the js part of a chunk */
|
||||||
u: (chunkId: string | number) => string;
|
u: (this: WebpackRequire, chunkId: PropertyKey) => string;
|
||||||
/** The global object, will likely always be the window */
|
/** The global object, will likely always be the window */
|
||||||
g: Window;
|
g: Window;
|
||||||
/** Harmony module decorator. Decorates a module as an ES Module, and prevents Node.js "module.exports" from being set */
|
/** Harmony module decorator. Decorates a module as an ES Module, and prevents Node.js "module.exports" from being set */
|
||||||
hmd: (module: Module) => any;
|
hmd: (this: WebpackRequire, module: Module) => any;
|
||||||
/** Shorthand for Object.prototype.hasOwnProperty */
|
/** Shorthand for Object.prototype.hasOwnProperty */
|
||||||
o: typeof Object.prototype.hasOwnProperty;
|
o: typeof Object.prototype.hasOwnProperty;
|
||||||
/**
|
/**
|
||||||
|
@ -114,11 +114,11 @@ type WebpackRequire = ((moduleId: PropertyKey) => Module) & {
|
||||||
* "done" will be attached to existing scripts loading if src === url or data-webpack === `${uniqueName}:${key}`,
|
* "done" will be attached to existing scripts loading if src === url or data-webpack === `${uniqueName}:${key}`,
|
||||||
* so it will be called when that existing script finishes loading.
|
* so it will be called when that existing script finishes loading.
|
||||||
*/
|
*/
|
||||||
l: (url: string, done: ScriptLoadDone, key?: string | number, chunkId?: string | number) => void;
|
l: (this: WebpackRequire, url: string, done: ScriptLoadDone, key?: string | number, chunkId?: PropertyKey) => void;
|
||||||
/** Defines __esModule on the exports, marking ES Modules compatibility as true */
|
/** Defines __esModule on the exports, marking ES Modules compatibility as true */
|
||||||
r: (exports: AnyRecord) => void;
|
r: (this: WebpackRequire, exports: AnyRecord) => 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: (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.
|
||||||
*
|
*
|
||||||
|
@ -135,7 +135,7 @@ type WebpackRequire = ((moduleId: PropertyKey) => Module) & {
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
v: (exports: AnyRecord, wasmModuleId: any, wasmModuleHash: string, importsObj?: WebAssembly.Imports) => Promise<any>;
|
v: (this: WebpackRequire, exports: AnyRecord, wasmModuleId: any, wasmModuleHash: string, importsObj?: WebAssembly.Imports) => Promise<any>;
|
||||||
/** Bundle public path, where chunk files are stored. Used by other methods which load chunks to obtain the full asset url */
|
/** Bundle public path, where chunk files are stored. Used by other methods which load chunks to obtain the full asset url */
|
||||||
p: string;
|
p: string;
|
||||||
/** Document baseURI or WebWorker location.href */
|
/** Document baseURI or WebWorker location.href */
|
||||||
|
|
Loading…
Reference in a new issue