diff --git a/src/components/PatchHelper.tsx b/src/components/PatchHelper.tsx
index 853febc5..391be9d0 100644
--- a/src/components/PatchHelper.tsx
+++ b/src/components/PatchHelper.tsx
@@ -80,7 +80,7 @@ function ReplacementComponent({ module, match, replacement, setReplacementError
const fullMatch = matchResult[0] ? makeCodeblock(matchResult[0], "js") : "";
const groups = matchResult.length > 1
- ? makeCodeblock(matchResult.slice(1).map((g, i) => `Group ${i}: ${g}`).join("\n"), "yml")
+ ? makeCodeblock(matchResult.slice(1).map((g, i) => `Group ${i + 1}: ${g}`).join("\n"), "yml")
: "";
return (
@@ -172,6 +172,22 @@ function ReplacementInput({ replacement, setReplacement, replacementError }) {
onChange={onChange}
error={error ?? replacementError}
/>
+ {!isFunc && (
+ <>
+ Cheat Sheet
+ {Object.entries({
+ "$$": "Insert a $",
+ "$&": "Insert the entire match",
+ "$\\`": "Insert the substring before the match",
+ "$'": "Insert the substring after the match",
+ "$n": "Insert the nth capturing group ($1, $2...)"
+ }).map(([placeholder, desc]) => (
+
+ {Parser.parse("`" + placeholder + "`")}: {desc}
+
+ ))}
+ >
+ )}
= Promise.resolve();
- add(func: () => Promisable) {
- this.promise = this.promise.then(func);
+ add(func: (lastValue: unknown) => Promisable): Promise {
+ return (this.promise = this.promise.then(func));
}
}
diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts
index 3db4dd2d..ba2d5591 100644
--- a/src/webpack/webpack.ts
+++ b/src/webpack/webpack.ts
@@ -21,6 +21,8 @@ import type { WebpackInstance } from "discord-types/other";
import Logger from "../utils/logger";
import { proxyLazy } from "../utils/proxyLazy";
+const logger = new Logger("Webpack");
+
export let _resolveReady: () => void;
/**
* Fired once a gateway connection to Discord has been established.
@@ -51,7 +53,6 @@ export const filters = {
},
};
-const logger = new Logger("Webpack");
export const subscriptions = new Map();
export const listeners = new Set();
@@ -126,51 +127,143 @@ export function findAll(filter: FilterFn, getDefault = true) {
}
/**
- * Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module)
- * then maps it into an easily usable module via the specified mappers
- * @param code Code snippet
- * @param mappers Mappers to create the non mangled exports
- * @returns Unmangled exports as specified in mappers
- *
- * @example mapMangledModule("headerIdIsManaged:", {
- * openModal: filters.byCode("headerIdIsManaged:"),
- * closeModal: filters.byCode("key==")
- * })
- */
-export function mapMangledModule(code: string, mappers: Record): Record {
- const exports = {} as Record;
+ * Same as {@link find} but in bulk
+ * @param filterFns Arry of filters. Please note that this array will be modified in place, so if you still
+ * need it afterwards, pass a copy.
+ * @returns Array of results in the same order as the passed filters
+ */
+export function bulk(...filterFns: FilterFn[]) {
+ if (!Array.isArray(filterFns))
+ throw new Error("Invalid filters. Expected function[] got " + typeof filterFns);
- // search every factory function
- for (const id in wreq.m) {
- const src = wreq.m[id].toString() as string;
- if (src.includes(code)) {
- const mod = wreq(id as any as number);
- outer:
- for (const key in mod) {
- const member = mod[key];
- for (const newName in mappers) {
- // if the current mapper matches this module
- if (mappers[newName](member)) {
- exports[newName] = member;
+ const { length } = filterFns;
+
+ if (length === 0)
+ throw new Error("Expected at least two filters.");
+
+ if (length === 1) {
+ if (IS_DEV) {
+ throw new Error("bulk called with only one filter. Use find");
+ }
+ return find(filterFns[0]);
+ }
+
+ const filters = filterFns as Array;
+
+ let found = 0;
+ const results = Array(length);
+
+ outer:
+ for (const key in cache) {
+ const mod = cache[key];
+ if (!mod?.exports) continue;
+
+ for (let j = 0; j < length; j++) {
+ const filter = filters[j];
+ // Already done
+ if (filter === undefined) continue;
+
+ if (filter(mod.exports)) {
+ results[j] = mod.exports;
+ filters[j] = undefined;
+ if (++found === length) break outer;
+ break;
+ }
+
+ if (typeof mod.exports !== "object")
+ continue;
+
+ if (mod.exports.default && filter(mod.exports.default)) {
+ results[j] = mod.exports.default;
+ filters[j] = undefined;
+ if (++found === length) break outer;
+ break;
+ }
+
+ for (const nestedMod in mod.exports)
+ if (nestedMod.length <= 3) {
+ const nested = mod.exports[nestedMod];
+ if (nested && filter(nested)) {
+ results[j] = nested;
+ filters[j] = undefined;
+ if (++found === length) break outer;
continue outer;
}
}
- }
- return exports;
}
}
- const err = new Error("Didn't find module matching this code:\n" + code);
- if (IS_DEV)
- throw err;
+ if (found !== length) {
+ const err = new Error(`Got ${length} filters, but only found ${found} modules!`);
+ if (IS_DEV) {
+ // Strict behaviour in DevBuilds to fail early and make sure the issue is found
+ throw err;
+ }
+ logger.warn(err);
+ }
+
+ return results;
+}
+
+/**
+ * Find the id of a module by its code
+ * @param code Code
+ * @returns number or null
+ */
+export function findModuleId(code: string) {
+ for (const id in wreq.m) {
+ if (wreq.m[id].toString().includes(code)) {
+ return Number(id);
+ }
+ }
+ const err = new Error("Didn't find module with code:\n" + code);
+ if (IS_DEV) {
+ // Strict behaviour in DevBuilds to fail early and make sure the issue is found
+ throw err;
+ }
logger.warn(err);
+
+ return null;
+}
+
+/**
+ * Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module)
+ * then maps it into an easily usable module via the specified mappers
+ * @param code Code snippet
+ * @param mappers Mappers to create the non mangled exports
+ * @returns Unmangled exports as specified in mappers
+ *
+ * @example mapMangledModule("headerIdIsManaged:", {
+ * openModal: filters.byCode("headerIdIsManaged:"),
+ * closeModal: filters.byCode("key==")
+ * })
+ */
+export function mapMangledModule(code: string, mappers: Record): Record {
+ const exports = {} as Record;
+
+ const id = findModuleId(code);
+ if (id === null)
+ return exports;
+
+ const mod = wreq(id);
+ outer:
+ for (const key in mod) {
+ const member = mod[key];
+ for (const newName in mappers) {
+ // if the current mapper matches this module
+ if (mappers[newName](member)) {
+ exports[newName] = member;
+ continue outer;
+ }
+ }
+ }
return exports;
}
/**
- * Same as {@link mapMangledModule} but lazy
- */
+ * Same as {@link mapMangledModule} but lazy
+ */
export function mapMangledModuleLazy(code: string, mappers: Record): Record {
return proxyLazy(() => mapMangledModule(code, mappers));
}
@@ -210,11 +303,11 @@ export function removeListener(callback: CallbackFn) {
}
/**
- * Search modules by keyword. This searches the factory methods,
- * meaning you can search all sorts of things, displayName, methodName, strings somewhere in the code, etc
- * @param filters One or more strings or regexes
- * @returns Mapping of found modules
- */
+ * Search modules by keyword. This searches the factory methods,
+ * meaning you can search all sorts of things, displayName, methodName, strings somewhere in the code, etc
+ * @param filters One or more strings or regexes
+ * @returns Mapping of found modules
+ */
export function search(...filters: Array) {
const results = {} as Record;
const factories = wreq.m;
@@ -233,13 +326,13 @@ export function search(...filters: Array) {
}
/**
- * Extract a specific module by id into its own Source File. This has no effect on
- * the code, it is only useful to be able to look at a specific module without having
- * to view a massive file. extract then returns the extracted module so you can jump to it.
- * As mentioned above, note that this extracted module is not actually used,
- * so putting breakpoints or similar will have no effect.
- * @param id The id of the module to extract
- */
+ * Extract a specific module by id into its own Source File. This has no effect on
+ * the code, it is only useful to be able to look at a specific module without having
+ * to view a massive file. extract then returns the extracted module so you can jump to it.
+ * As mentioned above, note that this extracted module is not actually used,
+ * so putting breakpoints or similar will have no effect.
+ * @param id The id of the module to extract
+ */
export function extract(id: number) {
const mod = wreq.m[id] as Function;
if (!mod) return null;