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

View file

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

View file

@ -678,16 +678,18 @@ export const _cacheFind = traceFunction("cacheFind", function _cacheFind(filter:
const mod = cache[key]; const mod = cache[key];
if (!mod?.loaded || mod?.exports == null) continue; if (!mod?.loaded || mod?.exports == null) continue;
const factory = wreq.m[key] as AnyModuleFactory;
if (filter.$$vencordIsFactoryFilter) { if (filter.$$vencordIsFactoryFilter) {
if (filter(wreq.m[key])) { 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; continue;
} }
if (filter(mod.exports)) { 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") { 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)) { 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) { for (const exportKey in mod.exports) if (exportKey.length <= 3) {
const exportValue = mod.exports[exportKey]; const exportValue = mod.exports[exportKey];
if (exportValue != null && filter(exportValue)) { 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 * @returns The found export or module exports, or undefined
*/ */
export function cacheFind(filter: FilterFn) { export function cacheFind(filter: FilterFn) {
const cacheFindResult = _cacheFind(filter); return _cacheFind(filter).result;
return cacheFindResult.result;
} }
/** /**
* Find the the export or module exports from an all the required modules that match the filter. * 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 * @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") { if (typeof filter !== "function") {
throw new Error("Invalid filter. Expected a function got " + typeof filter); throw new Error("Invalid filter. Expected a function got " + typeof filter);
} }
const ret: ModuleExports[] = []; const results: Required<CacheFindResult>[] = [];
for (const key in cache) { for (const key in cache) {
const mod = cache[key]; const mod = cache[key];
if (!mod?.loaded || mod?.exports == null) continue; if (!mod?.loaded || mod?.exports == null) continue;
const factory = wreq.m[key] as AnyModuleFactory;
if (filter.$$vencordIsFactoryFilter) { if (filter.$$vencordIsFactoryFilter) {
if (filter(wreq.m[key])) { if (filter(wreq.m[key])) {
ret.push(mod.exports); results.push({ result: mod.exports, id: key, exportKey: null, factory });
} }
continue; continue;
} }
if (filter(mod.exports)) { if (filter(mod.exports)) {
ret.push(mod.exports); results.push({ result: mod.exports, id: key, exportKey: null, factory });
} }
if (typeof mod.exports !== "object") { if (typeof mod.exports !== "object") {
@ -755,57 +757,54 @@ export function cacheFindAll(filter: FilterFn) {
} }
if (mod.exports.default != null && filter(mod.exports.default)) { 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) { for (const exportKey in mod.exports) if (exportKey.length <= 3) {
const exportValue = mod.exports[exportKey]; const exportValue = mod.exports[exportKey];
if (exportValue != null && filter(exportValue)) { if (exportValue != null && filter(exportValue)) {
ret.push(exportValue); results.push({ result: exportValue, id: key, exportKey, factory });
break; 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. * 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) { export function cacheFindModuleId(...code: CodeFilter) {
const parsedCode = code.map(canonicalizeMatch); return _cacheFind(filters.byFactoryCode(...code)).id;
for (const id in wreq.m) {
if (stringMatches(String(wreq.m[id]), parsedCode)) {
return 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. * meaning you can search all sorts of things, methodName, strings somewhere in the code, etc.
* *
* @param code One or more strings or regexes * @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) { export function searchFactories(...code: CodeFilter) {
code = code.map(canonicalizeMatch); const filter = filters.byFactoryCode(...code);
const results: WebpackRequire["m"] = {}; return _cacheFindAll(filter).reduce((results, { factory, id }) => {
const factories = wreq.m;
for (const id in factories) {
const factory = factories[id];
if (stringMatches(String(factory), code)) {
results[id] = factory; 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) { export function extract(id: PropertyKey) {
const factory = wreq.m[id]; const factory = wreq.m[id];
if (!factory) return null; if (factory == null) return null;
const code = ` const code = `
// [EXTRACTED] WebpackModule${String(id)} // [EXTRACTED] WebpackModule${String(id)}
@ -833,6 +832,8 @@ export function extract(id: PropertyKey) {
return extracted; return extracted;
} }
/* ------------------------------- Deprecations ------------------------------- */
/** /**
* @deprecated Use separate finds instead * @deprecated Use separate finds instead
* Same as {@link cacheFind} but in bulk. * Same as {@link cacheFind} but in bulk.
@ -1040,7 +1041,7 @@ export const mapMangledModuleLazy = deprecatedRedirect("mapMangledModuleLazy", "
export const findAll = deprecatedRedirect("findAll", "cacheFindAll", cacheFindAll); export const findAll = deprecatedRedirect("findAll", "cacheFindAll", cacheFindAll);
/** /**
* @deprecated Use {@link cacheFindBulk} instead * @deprecated Use separate finds instead
* *
* Same as {@link cacheFind} but in bulk * Same as {@link cacheFind} but in bulk
* *
@ -1057,3 +1058,14 @@ export const findBulk = deprecatedRedirect("findBulk", "cacheFindBulk", cacheFin
* @returns string or null * @returns string or null
*/ */
export const findModuleId = deprecatedRedirect("findModuleId", "cacheFindModuleId", cacheFindModuleId); 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 // inline require to avoid including it in !IS_DEV builds
const diff = (require("diff") as typeof import("diff")).diffWordsWithSpace(context, patchedContext); const diff = (require("diff") as typeof import("diff")).diffWordsWithSpace(context, patchedContext);
let fmt = "%c %s "; let fmt = "%c %s ";
const elements = [] as string[]; const elements: string[] = [];
for (const d of diff) { for (const d of diff) {
const color = d.removed const color = d.removed
? "red" ? "red"