minor cleanup

This commit is contained in:
Nuckyz 2024-08-23 04:55:09 -03:00
parent b1ad0a0d58
commit 26a71caced
No known key found for this signature in database
GPG key ID: 440BF8296E1C4AD9
5 changed files with 115 additions and 86 deletions

View file

@ -22,7 +22,7 @@ import { Margins } from "@utils/margins";
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
import { makeCodeblock } from "@utils/text";
import { Patch, ReplaceFn } from "@utils/types";
import { search } from "@webpack";
import { searchFactories } from "@webpack";
import { Button, Clipboard, Forms, Parser, React, Switch, TextArea, TextInput } from "@webpack/common";
import { SettingsTab, wrapTab } from "./shared";
@ -33,7 +33,7 @@ if (IS_DEV) {
}
const findCandidates = debounce(function ({ find, setModule, setError }) {
const candidates = search(find);
const candidates = searchFactories(find);
const keys = Object.keys(candidates);
const len = keys.length;
if (len === 0)

View file

@ -25,7 +25,7 @@ import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from
import { SYM_PROXY_INNER_GET, SYM_PROXY_INNER_VALUE } from "@utils/proxyInner";
import definePlugin, { PluginNative, StartAt } from "@utils/types";
import * as Webpack from "@webpack";
import { cacheFindAll, cacheFindModuleId, extract, filters, search } from "@webpack";
import { _cacheFindAll, cacheFindAll, cacheFindModuleId, extract, filters, searchFactories } from "@webpack";
import * as Common from "@webpack/common";
import { loadLazyChunks } from "debug/loadLazyChunks";
import type { ComponentType } from "react";
@ -34,85 +34,101 @@ const DESKTOP_ONLY = (f: string) => () => {
throw new Error(`'${f}' is Discord Desktop only.`);
};
const define: typeof Object.defineProperty =
(obj, prop, desc) => {
if (Object.hasOwn(desc, "value"))
desc.writable = true;
return Object.defineProperty(obj, prop, {
configurable: true,
enumerable: true,
...desc
});
};
type Define = typeof Object.defineProperty;
const define: Define = (target, p, attributes) => {
if (Object.hasOwn(attributes, "value")) {
attributes.writable = true;
}
return Object.defineProperty(target, p, {
configurable: true,
enumerable: true,
...attributes
});
};
function makeShortcuts() {
function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn) {
function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn, shouldReturnFactory = false) {
const cache = new Map<string, unknown>();
return function (...filterProps: unknown[]) {
const cacheKey = String(filterProps);
if (cache.has(cacheKey)) return cache.get(cacheKey);
const matches = cacheFindAll(filterFactory(...filterProps));
const matches = _cacheFindAll(filterFactory(...filterProps));
const result = (() => {
switch (matches.length) {
case 0: return null;
case 1: return matches[0];
case 1: return shouldReturnFactory ? matches[0].factory : matches[0].result;
default:
const uniqueMatches = [...new Set(matches)];
if (uniqueMatches.length > 1)
const uniqueMatches = [...new Set(shouldReturnFactory ? matches.map(m => m.factory) : matches.map(m => m.result))];
if (uniqueMatches.length > 1) {
console.warn(`Warning: This filter matches ${matches.length} modules. Make it more specific!\n`, uniqueMatches);
}
return matches[0];
return uniqueMatches[0];
}
})();
if (result && cacheKey) cache.set(cacheKey, result);
return result;
};
}
let fakeRenderWin: WeakRef<Window> | undefined;
const find = newFindWrapper(f => f);
const findByProps = newFindWrapper(filters.byProps);
return {
...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])),
wp: Webpack,
wpc: { getter: () => Webpack.cache },
wreq: { getter: () => Webpack.wreq },
WebpackInstances: { getter: () => Vencord.WebpackPatcher.allWebpackInstances },
wpsearch: search,
wpex: extract,
wpexs: (code: string) => extract(cacheFindModuleId(code)!),
loadLazyChunks: IS_DEV ? loadLazyChunks : () => { throw new Error("loadLazyChunks is dev only."); },
wpsearch: searchFactories,
wpex: extract,
wpexs: (...code: Webpack.CodeFilter) => extract(cacheFindModuleId(...code)!),
filters,
find,
findAll: cacheFindAll,
findByProps,
findAllByProps: (...props: string[]) => cacheFindAll(filters.byProps(...props)),
findProp: (...props: string[]) => findByProps(...props)[props[0]],
findByCode: newFindWrapper(filters.byCode),
findAllByCode: (code: string) => cacheFindAll(filters.byCode(code)),
findComponent: find,
findAllComponents: cacheFindAll,
findExportedComponent: (...props: Webpack.PropsFilter) => findByProps(...props)[props[0]],
findComponentByCode: newFindWrapper(filters.componentByCode),
findAllComponentsByCode: (...code: string[]) => cacheFindAll(filters.componentByCode(...code)),
findAllComponentsByCode: (...code: Webpack.PropsFilter) => cacheFindAll(filters.componentByCode(...code)),
findComponentByFields: newFindWrapper(filters.componentByFields),
findAllComponentsByFields: (...fields: string[]) => cacheFindAll(filters.componentByFields(...fields)),
findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]],
findAllComponentsByFields: (...fields: Webpack.PropsFilter) => cacheFindAll(filters.componentByFields(...fields)),
findByProps,
findAllByProps: (...props: Webpack.PropsFilter) => cacheFindAll(filters.byProps(...props)),
findProp: (...props: Webpack.PropsFilter) => findByProps(...props)[props[0]],
findByCode: newFindWrapper(filters.byCode),
findAllByCode: (code: Webpack.CodeFilter) => cacheFindAll(filters.byCode(...code)),
findStore: newFindWrapper(filters.byStoreName),
findByFactoryCode: newFindWrapper(filters.byFactoryCode),
findAllByFactoryCode: (...code: string[]) => cacheFindAll(filters.byFactoryCode(...code)),
PluginsApi: { getter: () => Vencord.Plugins },
findAllByFactoryCode: (...code: Webpack.CodeFilter) => cacheFindAll(filters.byFactoryCode(...code)),
findModuleFactory: newFindWrapper(filters.byFactoryCode, true),
findAllModuleFactories: (...code: Webpack.CodeFilter) => _cacheFindAll(filters.byFactoryCode(...code)).map(m => m.factory),
plugins: { getter: () => Vencord.Plugins.plugins },
PluginsApi: { getter: () => Vencord.Plugins },
Settings: { getter: () => Vencord.Settings },
Api: { getter: () => Vencord.Api },
Util: { getter: () => Vencord.Util },
reload: () => location.reload(),
restart: IS_WEB ? DESKTOP_ONLY("restart") : relaunch,
canonicalizeMatch,
canonicalizeReplace,
canonicalizeReplacement,
preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true,
fakeRender: (component: ComponentType, props: any) => {
const prevWin = fakeRenderWin?.deref();
const win = prevWin?.closed === false
@ -142,8 +158,6 @@ function makeShortcuts() {
Common.ReactDOM.render(Common.React.createElement(component, props), doc.body.appendChild(document.createElement("div")));
},
preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true,
channel: { getter: () => getCurrentChannel(), preload: false },
channelId: { getter: () => Common.SelectedChannelStore.getChannelId(), preload: false },
guild: { getter: () => getCurrentGuild(), preload: false },
@ -152,6 +166,7 @@ function makeShortcuts() {
meId: { getter: () => Common.UserStore.getCurrentUser().id, preload: false },
messages: { getter: () => Common.MessageStore.getMessages(Common.SelectedChannelStore.getChannelId()), preload: false },
...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])),
Stores: {
getter: () => Object.fromEntries(
Common.Flux.Store.getAll()
@ -213,8 +228,10 @@ export default definePlugin({
const shortcuts = makeShortcuts();
window.shortcutList = {};
for (const [key, val] of Object.entries(shortcuts)) {
if ("getter" in val) {
for (const key in shortcuts) {
const val = shortcuts[key];
if (Object.hasOwn(val, "getter")) {
define(window.shortcutList, key, {
get: () => loadAndCacheShortcut(key, val, true)
});
@ -228,7 +245,7 @@ export default definePlugin({
}
}
// unproxy loaded modules
// Unproxy loaded modules
Webpack.onceDiscordLoaded.then(() => {
setTimeout(() => this.eagerLoad(false), 1000);
@ -243,10 +260,10 @@ export default definePlugin({
await Webpack.onceDiscordLoaded;
const shortcuts = makeShortcuts();
for (const key in shortcuts) {
const val = shortcuts[key];
for (const [key, val] of Object.entries(shortcuts)) {
if (!Object.hasOwn(val, "getter") || (val as any).preload === false) continue;
if (!Object.hasOwn(val, "getter") || val.preload === false) continue;
try {
loadAndCacheShortcut(key, val, forceLoad);
} catch { } // swallow not found errors in DEV

View file

@ -22,7 +22,7 @@ import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger";
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
import { cacheFindAll, filters, search } from "@webpack";
import { cacheFindAll, filters, searchFactories } from "@webpack";
const PORT = 8485;
const NAV_ID = "dev-companion-reconnect";
@ -154,7 +154,7 @@ function initWs(isManual = false) {
case "testPatch": {
const { find, replacement } = data as PatchData;
const candidates = search(find);
const candidates = searchFactories(find);
const keys = Object.keys(candidates);
if (keys.length !== 1)
return reply("Expected exactly one 'find' matches, found " + keys.length);
@ -213,7 +213,7 @@ function initWs(isManual = false) {
results = cacheFindAll(filters.byCode(...parsedArgs));
break;
case "ModuleId":
results = Object.keys(search(parsedArgs[0]));
results = Object.keys(searchFactories(parsedArgs[0]));
break;
case "ComponentByCode":
results = cacheFindAll(filters.componentByCode(...parsedArgs));

View file

@ -678,16 +678,18 @@ export const _cacheFind = traceFunction("cacheFind", function _cacheFind(filter:
const mod = cache[key];
if (!mod?.loaded || mod?.exports == null) continue;
const factory = wreq.m[key] as AnyModuleFactory;
if (filter.$$vencordIsFactoryFilter) {
if (filter(wreq.m[key])) {
return { result: mod.exports, id: key, exportKey: null, factory: wreq.m[key] as AnyModuleFactory };
return { result: mod.exports, id: key, exportKey: null, factory };
}
continue;
}
if (filter(mod.exports)) {
return { result: mod.exports, id: key, exportKey: null, factory: wreq.m[key] as AnyModuleFactory };
return { result: mod.exports, id: key, exportKey: null, factory };
}
if (typeof mod.exports !== "object") {
@ -695,14 +697,14 @@ export const _cacheFind = traceFunction("cacheFind", function _cacheFind(filter:
}
if (mod.exports.default != null && filter(mod.exports.default)) {
return { result: mod.exports.default, id: key, exportKey: "default ", factory: wreq.m[key] as AnyModuleFactory };
return { result: mod.exports.default, id: key, exportKey: "default ", factory };
}
for (const exportKey in mod.exports) if (exportKey.length <= 3) {
const exportValue = mod.exports[exportKey];
if (exportValue != null && filter(exportValue)) {
return { result: exportValue, id: key, exportKey, factory: wreq.m[key] as AnyModuleFactory };
return { result: exportValue, id: key, exportKey, factory };
}
}
}
@ -717,37 +719,37 @@ export const _cacheFind = traceFunction("cacheFind", function _cacheFind(filter:
* @returns The found export or module exports, or undefined
*/
export function cacheFind(filter: FilterFn) {
const cacheFindResult = _cacheFind(filter);
return cacheFindResult.result;
return _cacheFind(filter).result;
}
/**
* Find the the export or module exports from an all the required modules that match the filter.
*
* @param filter A function that takes an export or module exports and returns a boolean
* @returns An array of all the found export or module exports
*/
export function cacheFindAll(filter: FilterFn) {
export function _cacheFindAll(filter: FilterFn): Required<CacheFindResult>[] {
if (typeof filter !== "function") {
throw new Error("Invalid filter. Expected a function got " + typeof filter);
}
const ret: ModuleExports[] = [];
const results: Required<CacheFindResult>[] = [];
for (const key in cache) {
const mod = cache[key];
if (!mod?.loaded || mod?.exports == null) continue;
const factory = wreq.m[key] as AnyModuleFactory;
if (filter.$$vencordIsFactoryFilter) {
if (filter(wreq.m[key])) {
ret.push(mod.exports);
results.push({ result: mod.exports, id: key, exportKey: null, factory });
}
continue;
}
if (filter(mod.exports)) {
ret.push(mod.exports);
results.push({ result: mod.exports, id: key, exportKey: null, factory });
}
if (typeof mod.exports !== "object") {
@ -755,57 +757,54 @@ export function cacheFindAll(filter: FilterFn) {
}
if (mod.exports.default != null && filter(mod.exports.default)) {
ret.push(mod.exports.default);
results.push({ result: mod.exports.default, id: key, exportKey: "default ", factory });
}
for (const exportKey in mod.exports) if (exportKey.length <= 3) {
const exportValue = mod.exports[exportKey];
if (exportValue != null && filter(exportValue)) {
ret.push(exportValue);
results.push({ result: exportValue, id: key, exportKey, factory });
break;
}
}
}
return ret;
return results;
}
/**
* Find the the export or module exports from an all the required modules that match the filter.
*
* @param filter A function that takes an export or module exports and returns a boolean
* @returns An array of found exports or module exports
*/
export function cacheFindAll(filter: FilterFn) {
return _cacheFindAll(filter).map(({ result }) => result);
}
/**
* Find the id of the first already loaded module factory that includes all the given code.
*/
export const cacheFindModuleId = traceFunction("cacheFindModuleId", function cacheFindModuleId(...code: CodeFilter) {
const parsedCode = code.map(canonicalizeMatch);
for (const id in wreq.m) {
if (stringMatches(String(wreq.m[id]), parsedCode)) {
return id;
}
}
});
export function cacheFindModuleId(...code: CodeFilter) {
return _cacheFind(filters.byFactoryCode(...code)).id;
}
/**
* Search modules by keyword. This searches the factory methods,
* Search factories by keyword. This searches the source code of the module factories,
* meaning you can search all sorts of things, methodName, strings somewhere in the code, etc.
*
* @param code One or more strings or regexes
* @returns Mapping of found modules
* @returns Mapping of found factories by their module id
*/
export function search(...code: CodeFilter) {
code = code.map(canonicalizeMatch);
export function searchFactories(...code: CodeFilter) {
const filter = filters.byFactoryCode(...code);
const results: WebpackRequire["m"] = {};
const factories = wreq.m;
return _cacheFindAll(filter).reduce((results, { factory, id }) => {
results[id] = factory;
for (const id in factories) {
const factory = factories[id];
if (stringMatches(String(factory), code)) {
results[id] = factory;
}
}
return results;
return results;
}, {} as Record<PropertyKey, AnyModuleFactory>);
}
/**
@ -819,7 +818,7 @@ export function search(...code: CodeFilter) {
*/
export function extract(id: PropertyKey) {
const factory = wreq.m[id];
if (!factory) return null;
if (factory == null) return null;
const code = `
// [EXTRACTED] WebpackModule${String(id)}
@ -833,6 +832,8 @@ export function extract(id: PropertyKey) {
return extracted;
}
/* ------------------------------- Deprecations ------------------------------- */
/**
* @deprecated Use separate finds instead
* Same as {@link cacheFind} but in bulk.
@ -1040,7 +1041,7 @@ export const mapMangledModuleLazy = deprecatedRedirect("mapMangledModuleLazy", "
export const findAll = deprecatedRedirect("findAll", "cacheFindAll", cacheFindAll);
/**
* @deprecated Use {@link cacheFindBulk} instead
* @deprecated Use separate finds instead
*
* Same as {@link cacheFind} but in bulk
*
@ -1057,3 +1058,14 @@ export const findBulk = deprecatedRedirect("findBulk", "cacheFindBulk", cacheFin
* @returns string or null
*/
export const findModuleId = deprecatedRedirect("findModuleId", "cacheFindModuleId", cacheFindModuleId);
/**
* @deprecated Use {@link searchFactories} instead
*
* Search modules by keyword. This searches the factory methods,
* meaning you can search all sorts of things, methodName, strings somewhere in the code, etc.
*
* @param code One or more strings or regexes
* @returns Mapping of found modules
*/
export const search = deprecatedRedirect("search", "searchFactories", searchFactories);

View file

@ -481,7 +481,7 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory) {
// inline require to avoid including it in !IS_DEV builds
const diff = (require("diff") as typeof import("diff")).diffWordsWithSpace(context, patchedContext);
let fmt = "%c %s ";
const elements = [] as string[];
const elements: string[] = [];
for (const d of diff) {
const color = d.removed
? "red"