From 49be750a1a20dfbd8fdf520857e8195cd1736c3e Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Thu, 15 Aug 2024 10:48:56 -0400 Subject: [PATCH 01/22] this is horror code --- src/plugins/devCompanion.dev/index.tsx | 186 ++++++++++++++++++++++++- src/utils/constants.ts | 4 + 2 files changed, 183 insertions(+), 7 deletions(-) diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index a495907b..60098e1f 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -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 { filters, findAll, search } from "@webpack"; +import { filters, findAll, search, wreq } from "@webpack"; const PORT = 8485; const NAV_ID = "dev-companion-reconnect"; @@ -50,7 +50,23 @@ interface FunctionNode { type: "function"; value: string; } - +interface ExtractFindData { + extractType: string, + findType: string, + findArgs: string[]; +} +interface ExtractData { + idOrSearch: string | number; + extractType: string; +} +enum ExtractResponseType { + OK, + ERROR, + NOT_FOUND +} +interface ReloadData { + native: boolean; +} interface PatchData { find: string; replacement: { @@ -87,17 +103,52 @@ function parseNode(node: Node) { throw new Error("Unknown Node Type " + (node as any).type); } } - +type CodeFilter = Array; +const stringMatches = (s: string, filter: CodeFilter) => + filter.every(f => + typeof f === "string" + ? s.includes(f) + : f.test(s) + ); +function findModuleId(find: CodeFilter) { + const matches: string[] = []; + for (const id in wreq.m) { + if (stringMatches(wreq.m[id].toString(), find)) matches.push(id); + } + if (matches.length === 0) { + throw new Error("No Matches Found"); + } + if (matches.length !== 1) { + throw new Error("More than one match"); + } + return matches[0]; +} +interface SendData { + type: string, + data?: any, + status?: number, +} function initWs(isManual = false) { let wasConnected = isManual; let hasErrored = false; const ws = socket = new WebSocket(`ws://localhost:${PORT}`); + function replyData(data: T) { + ws.send(JSON.stringify(data)); + } + ws.addEventListener("open", () => { wasConnected = true; logger.info("Connected to WebSocket"); + // send module cache to vscode + + replyData({ + type: "moduleList", + data: JSON.stringify(Object.keys(wreq.m)) + }); + (settings.store.notifyOnAutoConnect || isManual) && showNotification({ title: "Dev Companion Connected", body: "Connected to WebSocket", @@ -116,7 +167,7 @@ function initWs(isManual = false) { title: "Dev Companion Error", body: (e as ErrorEvent).message || "No Error Message", color: "var(--status-danger, red)", - noPersist: true, + noPersist: true }); }); @@ -130,6 +181,12 @@ function initWs(isManual = false) { body: e.reason || "No Reason provided", color: "var(--status-danger, red)", noPersist: true, + onClick() { + setTimeout(() => { + socket?.close(1000, "Reconnecting"); + initWs(true); + }, 2500); + } }); }); @@ -140,7 +197,6 @@ function initWs(isManual = false) { logger.error("Invalid JSON:", err, "\n" + e.data); return; } - function reply(error?: string) { const data = { nonce, ok: !error } as Record; if (error) data.error = error; @@ -151,6 +207,121 @@ function initWs(isManual = false) { logger.info("Received Message:", type, "\n", data); switch (type) { + case "extract": { + const { extractType, idOrSearch } = data as ExtractData; + switch (extractType) { + case "id": { + console.log("ID!"); + let data; + if (typeof idOrSearch === "number") + data = wreq.m[idOrSearch]?.toString() || null; + else { + throw "fun times"; + } + if (!data) + replyData({ + type: "extract", + status: ExtractResponseType.NOT_FOUND + }); + else + replyData({ + type: "extract", + status: ExtractResponseType.OK, + data, + moduleNumber: idOrSearch + }); + + break; + } + case "search": { + try { + const moduleId = findModuleId([idOrSearch.toString()]); + const data = wreq.m[moduleId].toString(); + replyData({ + type: "extract", + status: ExtractResponseType.OK, + data, + moduleNumber: moduleId + }); + } catch (e) { + if (e instanceof Error) + return void replyData({ + type: "extract", + status: ExtractResponseType.ERROR, + data: e.message + }); + console.error(e); + } + break; + } + case "find": { + const { findType, findArgs } = data; + try { + var parsedArgs = findArgs.map(parseNode); + } catch (err) { + return reply("Failed to parse args: " + err); + } + + try { + let results: any[]; + switch (findType.replace("find", "").replace("Lazy", "")) { + case "": + results = findAll(parsedArgs[0]); + break; + case "ByProps": + results = findAll(filters.byProps(...parsedArgs)); + break; + case "Store": + results = findAll(filters.byStoreName(parsedArgs[0])); + break; + case "ByCode": + results = findAll(filters.byCode(...parsedArgs)); + break; + case "ModuleId": + results = Object.keys(search(parsedArgs[0])); + break; + case "ComponentByCode": + results = findAll(filters.componentByCode(...parsedArgs)); + break; + default: + return reply("Unknown Find Type " + findType); + } + + const uniqueResultsCount = new Set(results).size; + if (uniqueResultsCount === 0) throw "No results"; + if (uniqueResultsCount > 1) throw "Found more than one result! Make this filter more specific"; + // best name ever + const foundFind = [...results][0].toString(); + replyData({ + type: "extract", + find: true, + data: foundFind, + moduleNumber: findModuleId([foundFind]) + }); + } catch (err) { + return reply("Failed to find: " + err); + } + break; + } + default: + replyData({ + type: "extract", + status: ExtractResponseType.ERROR, + data: `Unknown Type: ${extractType}` + }); + break; + } + break; + } + case "reload": { + const { native } = data as ReloadData; + if (native) { + VesktopNative; + } else { + window.location.reload(); + } + break; + } case "testPatch": { const { find, replacement } = data as PatchData; @@ -191,8 +362,9 @@ function initWs(isManual = false) { } case "testFind": { const { type, args } = data as FindData; + let parsedArgs; try { - var parsedArgs = args.map(parseNode); + parsedArgs = args.map(parseNode); } catch (err) { return reply("Failed to parse args: " + err); } @@ -242,7 +414,7 @@ function initWs(isManual = false) { export default definePlugin({ name: "DevCompanion", description: "Dev Companion Plugin", - authors: [Devs.Ven], + authors: [Devs.Ven, Devs.sadan], reporterTestable: ReporterTestable.None, settings, diff --git a/src/utils/constants.ts b/src/utils/constants.ts index febb8f9a..b8df8a5d 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -546,6 +546,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "Lumap", id: 585278686291427338n, }, + sadan: { + name: "sadan", + id: 521819891141967883n + } } satisfies Record); // iife so #__PURE__ works correctly From 39ce554b26c58ba8bf286881a7ace6b55972fd7e Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Sat, 17 Aug 2024 23:30:24 -0400 Subject: [PATCH 02/22] guh --- src/plugins/devCompanion.dev/index.tsx | 73 ++++++++------------------ 1 file changed, 21 insertions(+), 52 deletions(-) diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index 60098e1f..1f894bf8 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -50,23 +50,7 @@ interface FunctionNode { type: "function"; value: string; } -interface ExtractFindData { - extractType: string, - findType: string, - findArgs: string[]; -} -interface ExtractData { - idOrSearch: string | number; - extractType: string; -} -enum ExtractResponseType { - OK, - ERROR, - NOT_FOUND -} -interface ReloadData { - native: boolean; -} + interface PatchData { find: string; replacement: { @@ -125,8 +109,9 @@ function findModuleId(find: CodeFilter) { } interface SendData { type: string, - data?: any, - status?: number, + data: any, + ok: boolean; + nonce?: number; } function initWs(isManual = false) { let wasConnected = isManual; @@ -146,7 +131,8 @@ function initWs(isManual = false) { replyData({ type: "moduleList", - data: JSON.stringify(Object.keys(wreq.m)) + data: Object.keys(wreq.m), + ok: true, }); (settings.store.notifyOnAutoConnect || isManual) && showNotification({ @@ -203,12 +189,16 @@ function initWs(isManual = false) { ws.send(JSON.stringify(data)); } + function replyData(data: T) { + data.nonce = nonce; + ws.send(JSON.stringify(data)); + } logger.info("Received Message:", type, "\n", data); switch (type) { case "extract": { - const { extractType, idOrSearch } = data as ExtractData; + const { extractType, idOrSearch } = data; switch (extractType) { case "id": { console.log("ID!"); @@ -216,17 +206,14 @@ function initWs(isManual = false) { if (typeof idOrSearch === "number") data = wreq.m[idOrSearch]?.toString() || null; else { - throw "fun times"; + return reply(`the provided moduleID is not a number. Got: ${typeof idOrSearch}`); } if (!data) - replyData({ - type: "extract", - status: ExtractResponseType.NOT_FOUND - }); + return reply(`Module(${idOrSearch}) not found`); else replyData({ type: "extract", - status: ExtractResponseType.OK, + ok: true, data, moduleNumber: idOrSearch }); @@ -239,18 +226,12 @@ function initWs(isManual = false) { const data = wreq.m[moduleId].toString(); replyData({ type: "extract", - status: ExtractResponseType.OK, + ok: true, data, - moduleNumber: moduleId + moduleNumber: +moduleId }); } catch (e) { - if (e instanceof Error) - return void replyData({ - type: "extract", - status: ExtractResponseType.ERROR, - data: e.message - }); - console.error(e); + reply("Error: " + String(e)); } break; } @@ -291,12 +272,13 @@ function initWs(isManual = false) { if (uniqueResultsCount === 0) throw "No results"; if (uniqueResultsCount > 1) throw "Found more than one result! Make this filter more specific"; // best name ever - const foundFind = [...results][0].toString(); + const foundFind: string = [...results][0].toString(); replyData({ type: "extract", + ok: true, find: true, data: foundFind, - moduleNumber: findModuleId([foundFind]) + moduleNumber: +findModuleId([foundFind]) }); } catch (err) { return reply("Failed to find: " + err); @@ -304,24 +286,11 @@ function initWs(isManual = false) { break; } default: - replyData({ - type: "extract", - status: ExtractResponseType.ERROR, - data: `Unknown Type: ${extractType}` - }); + reply(`Unknown Extract type. Got: ${extractType}`); break; } break; } - case "reload": { - const { native } = data as ReloadData; - if (native) { - VesktopNative; - } else { - window.location.reload(); - } - break; - } case "testPatch": { const { find, replacement } = data as PatchData; From 6c93b4d7ec8770fcbdc1bd63efaa65b82a7c6b7c Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Sun, 18 Aug 2024 10:09:38 -0400 Subject: [PATCH 03/22] update imports --- src/plugins/devCompanion.dev/index.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index 1f894bf8..dc063c26 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -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 { filters, findAll, search, wreq } from "@webpack"; +import { CodeFilter, filters, findAll, search, stringMatches, wreq } from "@webpack"; const PORT = 8485; const NAV_ID = "dev-companion-reconnect"; @@ -87,13 +87,7 @@ function parseNode(node: Node) { throw new Error("Unknown Node Type " + (node as any).type); } } -type CodeFilter = Array; -const stringMatches = (s: string, filter: CodeFilter) => - filter.every(f => - typeof f === "string" - ? s.includes(f) - : f.test(s) - ); +// we need to have our own because the one in webpack returns the first with no handling of more than one module function findModuleId(find: CodeFilter) { const matches: string[] = []; for (const id in wreq.m) { From 4250424040ac88c52cc03196d6a074872bde05b5 Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Mon, 19 Aug 2024 23:13:21 -0400 Subject: [PATCH 04/22] add type, formatting and refactor --- src/plugins/devCompanion.dev/index.tsx | 256 +++++++++++-------------- src/plugins/devCompanion.dev/util.tsx | 96 ++++++++++ 2 files changed, 206 insertions(+), 146 deletions(-) create mode 100644 src/plugins/devCompanion.dev/util.tsx diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index dc063c26..edfb0fec 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -22,7 +22,9 @@ import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; -import { CodeFilter, filters, findAll, search, stringMatches, wreq } from "@webpack"; +import { filters, findAll, search, wreq } from "@webpack"; + +import { extractModule, extractOrThrow, FindData, findModuleId, parseNode, PatchData, SendData } from "./util"; const PORT = 8485; const NAV_ID = "dev-companion-reconnect"; @@ -31,82 +33,19 @@ const logger = new Logger("DevCompanion"); let socket: WebSocket | undefined; -type Node = StringNode | RegexNode | FunctionNode; - -interface StringNode { - type: "string"; - value: string; -} - -interface RegexNode { - type: "regex"; - value: { - pattern: string; - flags: string; - }; -} - -interface FunctionNode { - type: "function"; - value: string; -} - -interface PatchData { - find: string; - replacement: { - match: StringNode | RegexNode; - replace: StringNode | FunctionNode; - }[]; -} - -interface FindData { - type: string; - args: Array; -} - -const settings = definePluginSettings({ +export const settings = definePluginSettings({ notifyOnAutoConnect: { description: "Whether to notify when Dev Companion has automatically connected.", type: OptionType.BOOLEAN, default: true + }, + usePatchedModule: { + description: "On extract requests, reply with the current patched module (if it is patched) instead of the original", + default: true, + type: OptionType.BOOLEAN, } }); -function parseNode(node: Node) { - switch (node.type) { - case "string": - return node.value; - case "regex": - return new RegExp(node.value.pattern, node.value.flags); - case "function": - // We LOVE remote code execution - // Safety: This comes from localhost only, which actually means we have less permissions than the source, - // since we're running in the browser sandbox, whereas the sender has host access - return (0, eval)(node.value); - default: - throw new Error("Unknown Node Type " + (node as any).type); - } -} -// we need to have our own because the one in webpack returns the first with no handling of more than one module -function findModuleId(find: CodeFilter) { - const matches: string[] = []; - for (const id in wreq.m) { - if (stringMatches(wreq.m[id].toString(), find)) matches.push(id); - } - if (matches.length === 0) { - throw new Error("No Matches Found"); - } - if (matches.length !== 1) { - throw new Error("More than one match"); - } - return matches[0]; -} -interface SendData { - type: string, - data: any, - ok: boolean; - nonce?: number; -} function initWs(isManual = false) { let wasConnected = isManual; let hasErrored = false; @@ -191,97 +130,122 @@ function initWs(isManual = false) { logger.info("Received Message:", type, "\n", data); switch (type) { - case "extract": { + case "diff": { const { extractType, idOrSearch } = data; switch (extractType) { case "id": { - console.log("ID!"); - let data; - if (typeof idOrSearch === "number") - data = wreq.m[idOrSearch]?.toString() || null; - else { - return reply(`the provided moduleID is not a number. Got: ${typeof idOrSearch}`); - } - if (!data) - return reply(`Module(${idOrSearch}) not found`); - else - replyData({ - type: "extract", - ok: true, - data, - moduleNumber: idOrSearch - }); - + if (typeof idOrSearch !== "number") + throw new Error("Id is not a number, got :" + typeof idOrSearch); + replyData({ + type: "diff", + ok: true, + data: { + patched: extractOrThrow(idOrSearch), + source: extractModule(idOrSearch, false) + }, + moduleNumber: idOrSearch + }); break; } case "search": { - try { - const moduleId = findModuleId([idOrSearch.toString()]); - const data = wreq.m[moduleId].toString(); + const moduleId = +findModuleId([idOrSearch.toString()]); + replyData({ + type: "diff", + ok: true, + data: { + patched: extractOrThrow(moduleId), + source: extractModule(moduleId, false) + }, + moduleNumber: moduleId + }); + break; + } + } + break; + } + case "extract": { + try { + const { extractType, idOrSearch } = data; + switch (extractType) { + case "id": { + if (typeof idOrSearch !== "number") + throw new Error("Id is not a number, got :" + typeof idOrSearch); + else + replyData({ + type: "extract", + ok: true, + data: extractModule(idOrSearch), + moduleNumber: idOrSearch + }); + + break; + } + case "search": { + const moduleId = +findModuleId([idOrSearch.toString()]); replyData({ type: "extract", ok: true, - data, - moduleNumber: +moduleId + data: extractModule(moduleId), + moduleNumber: moduleId }); - } catch (e) { - reply("Error: " + String(e)); + break; } - break; - } - case "find": { - const { findType, findArgs } = data; - try { - var parsedArgs = findArgs.map(parseNode); - } catch (err) { - return reply("Failed to parse args: " + err); - } - - try { - let results: any[]; - switch (findType.replace("find", "").replace("Lazy", "")) { - case "": - results = findAll(parsedArgs[0]); - break; - case "ByProps": - results = findAll(filters.byProps(...parsedArgs)); - break; - case "Store": - results = findAll(filters.byStoreName(parsedArgs[0])); - break; - case "ByCode": - results = findAll(filters.byCode(...parsedArgs)); - break; - case "ModuleId": - results = Object.keys(search(parsedArgs[0])); - break; - case "ComponentByCode": - results = findAll(filters.componentByCode(...parsedArgs)); - break; - default: - return reply("Unknown Find Type " + findType); + case "find": { + const { findType, findArgs } = data; + try { + var parsedArgs = findArgs.map(parseNode); + } catch (err) { + return reply("Failed to parse args: " + err); } - const uniqueResultsCount = new Set(results).size; - if (uniqueResultsCount === 0) throw "No results"; - if (uniqueResultsCount > 1) throw "Found more than one result! Make this filter more specific"; - // best name ever - const foundFind: string = [...results][0].toString(); - replyData({ - type: "extract", - ok: true, - find: true, - data: foundFind, - moduleNumber: +findModuleId([foundFind]) - }); - } catch (err) { - return reply("Failed to find: " + err); + try { + let results: any[]; + switch (findType.replace("find", "").replace("Lazy", "")) { + case "": + results = findAll(parsedArgs[0]); + break; + case "ByProps": + results = findAll(filters.byProps(...parsedArgs)); + break; + case "Store": + results = findAll(filters.byStoreName(parsedArgs[0])); + break; + case "ByCode": + results = findAll(filters.byCode(...parsedArgs)); + break; + case "ModuleId": + results = Object.keys(search(parsedArgs[0])); + break; + case "ComponentByCode": + results = findAll(filters.componentByCode(...parsedArgs)); + break; + default: + return reply("Unknown Find Type " + findType); + } + + const uniqueResultsCount = new Set(results).size; + if (uniqueResultsCount === 0) throw "No results"; + if (uniqueResultsCount > 1) throw "Found more than one result! Make this filter more specific"; + // best name ever + const foundFind: string = [...results][0].toString(); + replyData({ + type: "extract", + ok: true, + find: true, + data: foundFind, + moduleNumber: +findModuleId([foundFind]) + }); + } catch (err) { + return reply("Failed to find: " + err); + } + break; } - break; + default: + reply(`Unknown Extract type. Got: ${extractType}`); + break; } - default: - reply(`Unknown Extract type. Got: ${extractType}`); - break; + } catch (error) { + reply(String(error)); } break; } diff --git a/src/plugins/devCompanion.dev/util.tsx b/src/plugins/devCompanion.dev/util.tsx new file mode 100644 index 00000000..6240e49c --- /dev/null +++ b/src/plugins/devCompanion.dev/util.tsx @@ -0,0 +1,96 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { CodeFilter, stringMatches, wreq } from "@webpack"; + +import { settings } from "."; + +type Node = StringNode | RegexNode | FunctionNode; + +export interface StringNode { + type: "string"; + value: string; +} +export interface RegexNode { + type: "regex"; + value: { + pattern: string; + flags: string; + }; +} +export interface FunctionNode { + type: "function"; + value: string; +} +export interface PatchData { + find: string; + replacement: { + match: StringNode | RegexNode; + replace: StringNode | FunctionNode; + }[]; +} +export interface FindData { + type: string; + args: Array; +}export interface SendData { + type: string; + data: any; + ok: boolean; + nonce?: number; +} +/** + * extracts the patched module, if there is no patched module, throws an error + * @param id module id + */ +export function extractOrThrow(id) { + const module = wreq.m[id]; + if (!module?.$$vencordPatchedSource) + throw new Error("No patched module found for module id " + id); + return module.$$vencordPatchedSource; +} +/** + * attempts to extract the module, throws if not found + * + * + * if patched is true and no patched module is found fallsback to the non-patched module + * @param id module id + * @param patched return the patched module + */ +export function extractModule(id: number, patched = settings.store.usePatchedModule): string { + const module = wreq.m[id]; + if (!module) + throw new Error("No module found for module id:" + id); + return patched ? module.$$vencordPatchedSource ?? module.original : module.original; +} export function parseNode(node: Node) { + switch (node.type) { + case "string": + return node.value; + case "regex": + return new RegExp(node.value.pattern, node.value.flags); + case "function": + // We LOVE remote code execution + // Safety: This comes from localhost only, which actually means we have less permissions than the source, + // since we're running in the browser sandbox, whereas the sender has host access + return (0, eval)(node.value); + default: + throw new Error("Unknown Node Type " + (node as any).type); + } +} +// we need to have our own because the one in webpack returns the first with no handling of more than one module +export function findModuleId(find: CodeFilter) { + const matches: string[] = []; + for (const id in wreq.m) { + if (stringMatches(wreq.m[id].toString(), find)) matches.push(id); + } + if (matches.length === 0) { + throw new Error("No Matches Found"); + } + if (matches.length !== 1) { + throw new Error("More than one match"); + } + return matches[0]; +} + From da01d52e50c8de44cea3f7964923c6b4cfe10f41 Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:37:47 -0400 Subject: [PATCH 05/22] add diffing and start work on reporter GUI --- src/debug/runReporter.ts | 34 +++++++++++++- src/plugins/devCompanion.dev/index.tsx | 63 ++++++++++++++------------ src/plugins/devCompanion.dev/util.tsx | 2 +- src/webpack/webpack.ts | 4 +- 4 files changed, 71 insertions(+), 32 deletions(-) diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index ddd5e5f1..2cce25bd 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -11,7 +11,36 @@ import { patches } from "plugins"; import { loadLazyChunks } from "./loadLazyChunks"; const ReporterLogger = new Logger("Reporter"); - +interface ReporterData { + failedPatches: { + /** + * pluginName > array of failed modules + */ + foundNoModule: Record; + }; + failedWebpack: Record; +} +const reporterData: ReporterData = { + failedPatches: { + foundNoModule: {} + }, + failedWebpack: { + find: [[]], + findByProps: [[]], + findByCode: [[]], + findStore: [[]], + findComponent: [[]], + findComponentByCode: [[]], + findExportedComponent: [[]], + waitFor: [[]], + waitForComponent: [[]], + waitForStore: [[]], + proxyLazyWebpack: [[]], + LazyComponentWebpack: [[]], + extractAndLoadChunks: [[]], + mapMangledModule: [[]] + } +}; async function runReporter() { try { ReporterLogger.log("Starting test..."); @@ -70,7 +99,7 @@ async function runReporter() { logMessage += `("${args[0]}", {\n${failedMappings.map(mapping => `\t${mapping}: ${args[1][mapping].toString().slice(0, 147)}...`).join(",\n")}\n})`; } else logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`; - + reporterData.failedWebpack[method].push(args.map(a => String(a))); ReporterLogger.log("Webpack Find Fail:", logMessage); } } @@ -82,3 +111,4 @@ async function runReporter() { } runReporter(); +console.log(reporterData); diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index edfb0fec..4a479ce5 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -131,35 +131,42 @@ function initWs(isManual = false) { switch (type) { case "diff": { - const { extractType, idOrSearch } = data; - switch (extractType) { - case "id": { - if (typeof idOrSearch !== "number") - throw new Error("Id is not a number, got :" + typeof idOrSearch); - replyData({ - type: "diff", - ok: true, - data: { - patched: extractOrThrow(idOrSearch), - source: extractModule(idOrSearch, false) - }, - moduleNumber: idOrSearch - }); - break; - } - case "search": { - const moduleId = +findModuleId([idOrSearch.toString()]); - replyData({ - type: "diff", - ok: true, - data: { - patched: extractOrThrow(moduleId), - source: extractModule(moduleId, false) - }, - moduleNumber: moduleId - }); - break; + try { + const { extractType, idOrSearch } = data; + switch (extractType) { + case "id": { + if (typeof idOrSearch !== "number") + throw new Error("Id is not a number, got :" + typeof idOrSearch); + replyData({ + type: "diff", + ok: true, + data: { + patched: extractOrThrow(idOrSearch), + source: extractModule(idOrSearch, false) + }, + moduleNumber: idOrSearch + }); + break; + } + case "search": { + const moduleId = +findModuleId([idOrSearch.toString()]); + const p = extractOrThrow(moduleId); + const p2 = extractModule(moduleId, false); + console.log(p, p2, "done"); + replyData({ + type: "diff", + ok: true, + data: { + patched: p, + source: p2 + }, + moduleNumber: moduleId + }); + break; + } } + } catch (error) { + reply(String(error)); } break; } diff --git a/src/plugins/devCompanion.dev/util.tsx b/src/plugins/devCompanion.dev/util.tsx index 6240e49c..a681c6eb 100644 --- a/src/plugins/devCompanion.dev/util.tsx +++ b/src/plugins/devCompanion.dev/util.tsx @@ -63,7 +63,7 @@ export function extractModule(id: number, patched = settings.store.usePatchedMod const module = wreq.m[id]; if (!module) throw new Error("No module found for module id:" + id); - return patched ? module.$$vencordPatchedSource ?? module.original : module.original; + return patched ? module.$$vencordPatchedSource ?? module.original.toString() : module.original.toString(); } export function parseNode(node: Node) { switch (node.type) { case "string": diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index 19519d64..bbc60de4 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -287,7 +287,9 @@ export function findModuleFactory(...code: CodeFilter) { return wreq.m[id]; } -export const lazyWebpackSearchHistory = [] as Array<["find" | "findByProps" | "findByCode" | "findStore" | "findComponent" | "findComponentByCode" | "findExportedComponent" | "waitFor" | "waitForComponent" | "waitForStore" | "proxyLazyWebpack" | "LazyComponentWebpack" | "extractAndLoadChunks" | "mapMangledModule", any[]]>; +// FIXME: give this a better name +export type TypeWebpackSearchHistory = "find" | "findByProps" | "findByCode" | "findStore" | "findComponent" | "findComponentByCode" | "findExportedComponent" | "waitFor" | "waitForComponent" | "waitForStore" | "proxyLazyWebpack" | "LazyComponentWebpack" | "extractAndLoadChunks" | "mapMangledModule"; +export const lazyWebpackSearchHistory = [] as Array<[TypeWebpackSearchHistory, any[]]>; /** * This is just a wrapper around {@link proxyLazy} to make our reporter test for your webpack finds. From 4a5d480acc0a971a94daf5802cd57aebe7d3b65b Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:03:02 -0400 Subject: [PATCH 06/22] support regex finds and bit more work on reporter gui --- scripts/build/build.mjs | 3 ++- scripts/build/common.mjs | 1 + src/debug/runReporter.ts | 30 ++++++++++++++++++-------- src/globals.d.ts | 1 + src/plugins/devCompanion.dev/index.tsx | 23 ++++++++++++++++---- src/plugins/devCompanion.dev/util.tsx | 10 +++++++++ src/webpack/patchWebpack.ts | 21 +++++++++++++++++- 7 files changed, 74 insertions(+), 15 deletions(-) diff --git a/scripts/build/build.mjs b/scripts/build/build.mjs index 623f9f94..5799ce16 100755 --- a/scripts/build/build.mjs +++ b/scripts/build/build.mjs @@ -21,12 +21,13 @@ import esbuild from "esbuild"; import { readdir } from "fs/promises"; import { join } from "path"; -import { BUILD_TIMESTAMP, commonOpts, exists, globPlugins, IS_DEV, IS_REPORTER, IS_STANDALONE, IS_UPDATER_DISABLED, resolvePluginName, VERSION, commonRendererPlugins, watch } from "./common.mjs"; +import { BUILD_TIMESTAMP, commonOpts, exists, globPlugins, IS_DEV, IS_REPORTER, IS_STANDALONE, IS_UPDATER_DISABLED, resolvePluginName, VERSION, commonRendererPlugins, watch, IS_COMPANION_TEST } from "./common.mjs"; const defines = { IS_STANDALONE, IS_DEV, IS_REPORTER, + IS_COMPANION_TEST, IS_UPDATER_DISABLED, IS_WEB: false, IS_EXTENSION: false, diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index 247557e3..d6ecb0c8 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -41,6 +41,7 @@ export const watch = process.argv.includes("--watch"); export const IS_DEV = watch || process.argv.includes("--dev"); export const IS_REPORTER = process.argv.includes("--reporter"); export const IS_STANDALONE = process.argv.includes("--standalone"); +export const IS_COMPANION_TEST = process.argv.includes("--companion-test"); export const IS_UPDATER_DISABLED = process.argv.includes("--disable-updater"); export const gitHash = process.env.VENCORD_HASH || execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim(); diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index 2cce25bd..5c135bf5 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -5,24 +5,31 @@ */ import { Logger } from "@utils/Logger"; +import { Patch } from "@utils/types"; import * as Webpack from "@webpack"; import { patches } from "plugins"; import { loadLazyChunks } from "./loadLazyChunks"; const ReporterLogger = new Logger("Reporter"); +interface EvaledPatch extends Patch { + id: number | string; +} interface ReporterData { failedPatches: { - /** - * pluginName > array of failed modules - */ - foundNoModule: Record; + foundNoModule: Patch[]; + hadNoEffect: EvaledPatch[]; + undoingPatchGroup: EvaledPatch[]; + erroredPatch: EvaledPatch[]; }; failedWebpack: Record; } -const reporterData: ReporterData = { +export const reporterData: ReporterData = { failedPatches: { - foundNoModule: {} + foundNoModule: [], + hadNoEffect: [], + undoingPatchGroup: [], + erroredPatch: [] }, failedWebpack: { find: [[]], @@ -54,6 +61,8 @@ async function runReporter() { for (const patch of patches) { if (!patch.all) { new Logger("WebpackInterceptor").warn(`Patch by ${patch.plugin} found no module (Module id is -): ${patch.find}`); + if (IS_COMPANION_TEST) + reporterData.failedPatches.foundNoModule[patch.plugin].push(String(patch.find)); } } @@ -99,7 +108,8 @@ async function runReporter() { logMessage += `("${args[0]}", {\n${failedMappings.map(mapping => `\t${mapping}: ${args[1][mapping].toString().slice(0, 147)}...`).join(",\n")}\n})`; } else logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`; - reporterData.failedWebpack[method].push(args.map(a => String(a))); + if (IS_COMPANION_TEST) + reporterData.failedWebpack[method].push(args.map(a => String(a))); ReporterLogger.log("Webpack Find Fail:", logMessage); } } @@ -108,7 +118,9 @@ async function runReporter() { } catch (e) { ReporterLogger.log("A fatal error occurred:", e); } + console.log(reporterData); } -runReporter(); -console.log(reporterData); +// imported in webpack for reporterData, wrap to avoid running reporter +if (IS_REPORTER) + runReporter(); diff --git a/src/globals.d.ts b/src/globals.d.ts index e20ca4b7..547bcaae 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -38,6 +38,7 @@ declare global { export var IS_UPDATER_DISABLED: boolean; export var IS_DEV: boolean; export var IS_REPORTER: boolean; + export var IS_COMPANION_TEST: boolean; export var IS_DISCORD_DESKTOP: boolean; export var IS_VESKTOP: boolean; export var VERSION: string; diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index 4a479ce5..961c8dbf 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -24,7 +24,7 @@ import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; import { filters, findAll, search, wreq } from "@webpack"; -import { extractModule, extractOrThrow, FindData, findModuleId, parseNode, PatchData, SendData } from "./util"; +import { extractModule, extractOrThrow, FindData, findModuleId, FindType, mkRegexFind, parseNode, PatchData, SendData } from "./util"; const PORT = 8485; const NAV_ID = "dev-companion-reconnect"; @@ -149,7 +149,11 @@ function initWs(isManual = false) { break; } case "search": { - const moduleId = +findModuleId([idOrSearch.toString()]); + let moduleId; + if (data.findType === FindType.STRING) + moduleId = +findModuleId([idOrSearch.toString()]); + else + moduleId = +findModuleId(mkRegexFind(idOrSearch)); const p = extractOrThrow(moduleId); const p2 = extractModule(moduleId, false); console.log(p, p2, "done"); @@ -188,7 +192,11 @@ function initWs(isManual = false) { break; } case "search": { - const moduleId = +findModuleId([idOrSearch.toString()]); + let moduleId; + if (data.findType === FindType.STRING) + moduleId = +findModuleId([idOrSearch.toString()]); + else + moduleId = +findModuleId(mkRegexFind(idOrSearch)); replyData({ type: "extract", ok: true, @@ -259,7 +267,13 @@ function initWs(isManual = false) { case "testPatch": { const { find, replacement } = data as PatchData; - const candidates = search(find); + let candidates; + if (data.findType === FindType.STRING) + candidates = search(find.toString()); + else + candidates = search(...mkRegexFind(find)); + + // const candidates = search(find); const keys = Object.keys(candidates); if (keys.length !== 1) return reply("Expected exactly one 'find' matches, found " + keys.length); @@ -368,3 +382,4 @@ export default definePlugin({ socket = void 0; } }); + diff --git a/src/plugins/devCompanion.dev/util.tsx b/src/plugins/devCompanion.dev/util.tsx index a681c6eb..a623f131 100644 --- a/src/plugins/devCompanion.dev/util.tsx +++ b/src/plugins/devCompanion.dev/util.tsx @@ -4,6 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { canonicalizeMatch } from "@utils/patches"; import { CodeFilter, stringMatches, wreq } from "@webpack"; import { settings } from "."; @@ -21,6 +22,10 @@ export interface RegexNode { flags: string; }; } +export enum FindType { + STRING, + REGEX +} export interface FunctionNode { type: "function"; value: string; @@ -93,4 +98,9 @@ export function findModuleId(find: CodeFilter) { } return matches[0]; } +export function mkRegexFind(idOrSearch: string): RegExp[] { + const regex = idOrSearch.substring(1, idOrSearch.lastIndexOf("/")); + const flags = idOrSearch.substring(idOrSearch.lastIndexOf("/") + 1); + return [canonicalizeMatch(RegExp(regex, flags))]; +} diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index fb640cea..8793683f 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -20,6 +20,7 @@ import { WEBPACK_CHUNK } from "@utils/constants"; import { Logger } from "@utils/Logger"; import { canonicalizeReplacement } from "@utils/patches"; import { PatchReplacement } from "@utils/types"; +import { reporterData } from "debug/runReporter"; import { WebpackInstance } from "discord-types/other"; import { traceFunction } from "../debug/Tracer"; @@ -287,6 +288,11 @@ function patchFactories(factories: Record Date: Tue, 20 Aug 2024 16:30:23 -0400 Subject: [PATCH 07/22] add error on conflicting args --- scripts/build/common.mjs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index d6ecb0c8..b0999d8c 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -41,7 +41,9 @@ export const watch = process.argv.includes("--watch"); export const IS_DEV = watch || process.argv.includes("--dev"); export const IS_REPORTER = process.argv.includes("--reporter"); export const IS_STANDALONE = process.argv.includes("--standalone"); -export const IS_COMPANION_TEST = process.argv.includes("--companion-test"); +export const IS_COMPANION_TEST = IS_REPORTER && process.argv.includes("--companion-test"); +if (!IS_COMPANION_TEST && process.argv.includes("--companion-test")) + console.error("--companion-test must be run with --reporter for any effect"); export const IS_UPDATER_DISABLED = process.argv.includes("--disable-updater"); export const gitHash = process.env.VENCORD_HASH || execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim(); From 7fe1b2dbd40f0cdac4ec28ff3bad64fdb8edd608 Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:30:50 -0400 Subject: [PATCH 08/22] fix after refactor && im stupid --- src/debug/runReporter.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index 5c135bf5..8a3a3c3d 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -32,20 +32,20 @@ export const reporterData: ReporterData = { erroredPatch: [] }, failedWebpack: { - find: [[]], - findByProps: [[]], - findByCode: [[]], - findStore: [[]], - findComponent: [[]], - findComponentByCode: [[]], - findExportedComponent: [[]], - waitFor: [[]], - waitForComponent: [[]], - waitForStore: [[]], - proxyLazyWebpack: [[]], - LazyComponentWebpack: [[]], - extractAndLoadChunks: [[]], - mapMangledModule: [[]] + find: [], + findByProps: [], + findByCode: [], + findStore: [], + findComponent: [], + findComponentByCode: [], + findExportedComponent: [], + waitFor: [], + waitForComponent: [], + waitForStore: [], + proxyLazyWebpack: [], + LazyComponentWebpack: [], + extractAndLoadChunks: [], + mapMangledModule: [] } }; async function runReporter() { @@ -62,7 +62,7 @@ async function runReporter() { if (!patch.all) { new Logger("WebpackInterceptor").warn(`Patch by ${patch.plugin} found no module (Module id is -): ${patch.find}`); if (IS_COMPANION_TEST) - reporterData.failedPatches.foundNoModule[patch.plugin].push(String(patch.find)); + reporterData.failedPatches.foundNoModule.push(patch); } } From 982b52d6e905b38c106f0b7cb1cb87143a75bc3d Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Tue, 20 Aug 2024 19:37:44 -0400 Subject: [PATCH 09/22] add reload, send tasks. add reporter and build tasks --- .vscode/tasks.json | 14 +++++++++++++- src/plugins/devCompanion.dev/index.tsx | 11 +++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 5b160cb8..13297d62 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -12,6 +12,18 @@ "isDefault": true } }, + { + // for use with the vencord companion extension + "label": "BuildCompanionReporter", + "type": "shell", + "command": "pnpm build --dev --reporter --companion-test" + }, + { + "label": "BuildDev", + "type": "shell", + "command": "pnpm build --dev", + "group": "build" + }, { "label": "Watch", "type": "shell", @@ -22,4 +34,4 @@ } } ] -} \ No newline at end of file +} diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index 961c8dbf..715a3240 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -23,6 +23,7 @@ import { Logger } from "@utils/Logger"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; import { filters, findAll, search, wreq } from "@webpack"; +import { reporterData } from "debug/runReporter"; import { extractModule, extractOrThrow, FindData, findModuleId, FindType, mkRegexFind, parseNode, PatchData, SendData } from "./util"; @@ -67,6 +68,12 @@ function initWs(isManual = false) { data: Object.keys(wreq.m), ok: true, }); + // if we are running the reporter with companion integration, send the list to vscode as soon as we can + replyData({ + type: "report", + data: reporterData, + ok: true + }); (settings.store.notifyOnAutoConnect || isManual) && showNotification({ title: "Dev Companion Connected", @@ -174,6 +181,10 @@ function initWs(isManual = false) { } break; } + case "reload": { + window.location.reload(); + break; + } case "extract": { try { const { extractType, idOrSearch } = data; From 4f92052d497bba85d7a056feffb708a3dec8699a Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Tue, 20 Aug 2024 20:58:47 -0400 Subject: [PATCH 10/22] fix imports causing a crash and add missing if statment --- .vscode/tasks.json | 13 +++++-- src/debug/reporterData.ts | 48 ++++++++++++++++++++++++++ src/debug/runReporter.ts | 38 +------------------- src/plugins/devCompanion.dev/index.tsx | 14 ++++---- src/webpack/patchWebpack.ts | 11 +++--- 5 files changed, 73 insertions(+), 51 deletions(-) create mode 100644 src/debug/reporterData.ts diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 13297d62..7706f6e5 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -14,12 +14,19 @@ }, { // for use with the vencord companion extension - "label": "BuildCompanionReporter", + "label": "Build Companion Reporter", "type": "shell", - "command": "pnpm build --dev --reporter --companion-test" + "command": "pnpm build --dev --reporter --companion-test", + "presentation": { + "echo": true, + "reveal": "silent", + "panel": "shared", + "showReuseMessage": true, + "clear": true + } }, { - "label": "BuildDev", + "label": "Build Dev", "type": "shell", "command": "pnpm build --dev", "group": "build" diff --git a/src/debug/reporterData.ts b/src/debug/reporterData.ts new file mode 100644 index 00000000..09cd318d --- /dev/null +++ b/src/debug/reporterData.ts @@ -0,0 +1,48 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +/** + * this file is needed to avoid an import of plugins in ./runReporter.ts + */ +import { Patch } from "@utils/types"; +import { TypeWebpackSearchHistory } from "@webpack"; + +interface EvaledPatch extends Patch { + id: number | string; +} +interface ReporterData { + failedPatches: { + foundNoModule: Patch[]; + hadNoEffect: EvaledPatch[]; + undoingPatchGroup: EvaledPatch[]; + erroredPatch: EvaledPatch[]; + }; + failedWebpack: Record; +} +export const reporterData: ReporterData = { + failedPatches: { + foundNoModule: [], + hadNoEffect: [], + undoingPatchGroup: [], + erroredPatch: [] + }, + failedWebpack: { + find: [], + findByProps: [], + findByCode: [], + findStore: [], + findComponent: [], + findComponentByCode: [], + findExportedComponent: [], + waitFor: [], + waitForComponent: [], + waitForStore: [], + proxyLazyWebpack: [], + LazyComponentWebpack: [], + extractAndLoadChunks: [], + mapMangledModule: [] + } +}; diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index 8a3a3c3d..2c6563b6 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -5,49 +5,13 @@ */ import { Logger } from "@utils/Logger"; -import { Patch } from "@utils/types"; import * as Webpack from "@webpack"; import { patches } from "plugins"; import { loadLazyChunks } from "./loadLazyChunks"; +import { reporterData } from "./reporterData"; const ReporterLogger = new Logger("Reporter"); -interface EvaledPatch extends Patch { - id: number | string; -} -interface ReporterData { - failedPatches: { - foundNoModule: Patch[]; - hadNoEffect: EvaledPatch[]; - undoingPatchGroup: EvaledPatch[]; - erroredPatch: EvaledPatch[]; - }; - failedWebpack: Record; -} -export const reporterData: ReporterData = { - failedPatches: { - foundNoModule: [], - hadNoEffect: [], - undoingPatchGroup: [], - erroredPatch: [] - }, - failedWebpack: { - find: [], - findByProps: [], - findByCode: [], - findStore: [], - findComponent: [], - findComponentByCode: [], - findExportedComponent: [], - waitFor: [], - waitForComponent: [], - waitForStore: [], - proxyLazyWebpack: [], - LazyComponentWebpack: [], - extractAndLoadChunks: [], - mapMangledModule: [] - } -}; async function runReporter() { try { ReporterLogger.log("Starting test..."); diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index 715a3240..ab2be99e 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -23,7 +23,7 @@ import { Logger } from "@utils/Logger"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; import { filters, findAll, search, wreq } from "@webpack"; -import { reporterData } from "debug/runReporter"; +import { reporterData } from "debug/reporterData"; import { extractModule, extractOrThrow, FindData, findModuleId, FindType, mkRegexFind, parseNode, PatchData, SendData } from "./util"; @@ -69,11 +69,13 @@ function initWs(isManual = false) { ok: true, }); // if we are running the reporter with companion integration, send the list to vscode as soon as we can - replyData({ - type: "report", - data: reporterData, - ok: true - }); + if (IS_COMPANION_TEST) { + replyData({ + type: "report", + data: reporterData, + ok: true + }); + } (settings.store.notifyOnAutoConnect || isManual) && showNotification({ title: "Dev Companion Connected", diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 8793683f..45c6970d 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -20,7 +20,7 @@ import { WEBPACK_CHUNK } from "@utils/constants"; import { Logger } from "@utils/Logger"; import { canonicalizeReplacement } from "@utils/patches"; import { PatchReplacement } from "@utils/types"; -import { reporterData } from "debug/runReporter"; +import { reporterData } from "debug/reporterData"; import { WebpackInstance } from "discord-types/other"; import { traceFunction } from "../debug/Tracer"; @@ -357,10 +357,11 @@ function patchFactories(factories: Record Date: Wed, 21 Aug 2024 01:41:47 -0400 Subject: [PATCH 11/22] refactor --- src/plugins/devCompanion.dev/index.tsx | 348 +---------------------- src/plugins/devCompanion.dev/initWs.tsx | 350 ++++++++++++++++++++++++ 2 files changed, 359 insertions(+), 339 deletions(-) create mode 100644 src/plugins/devCompanion.dev/initWs.tsx diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index ab2be99e..6e9a287d 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -16,23 +16,18 @@ * along with this program. If not, see . */ -import { showNotification } from "@api/Notifications"; import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; -import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; -import { filters, findAll, search, wreq } from "@webpack"; import { reporterData } from "debug/reporterData"; -import { extractModule, extractOrThrow, FindData, findModuleId, FindType, mkRegexFind, parseNode, PatchData, SendData } from "./util"; - -const PORT = 8485; +import { initWs, socket, stopWs } from "./initWs"; +console.log("imported"); +export const PORT = 8485; const NAV_ID = "dev-companion-reconnect"; -const logger = new Logger("DevCompanion"); - -let socket: WebSocket | undefined; +export const logger = new Logger("DevCompanion"); export const settings = definePluginSettings({ notifyOnAutoConnect: { @@ -47,336 +42,12 @@ export const settings = definePluginSettings({ } }); -function initWs(isManual = false) { - let wasConnected = isManual; - let hasErrored = false; - const ws = socket = new WebSocket(`ws://localhost:${PORT}`); - - function replyData(data: T) { - ws.send(JSON.stringify(data)); - } - - ws.addEventListener("open", () => { - wasConnected = true; - - logger.info("Connected to WebSocket"); - - // send module cache to vscode - - replyData({ - type: "moduleList", - data: Object.keys(wreq.m), - ok: true, - }); - // if we are running the reporter with companion integration, send the list to vscode as soon as we can - if (IS_COMPANION_TEST) { - replyData({ - type: "report", - data: reporterData, - ok: true - }); - } - - (settings.store.notifyOnAutoConnect || isManual) && showNotification({ - title: "Dev Companion Connected", - body: "Connected to WebSocket", - noPersist: true - }); - }); - - ws.addEventListener("error", e => { - if (!wasConnected) return; - - hasErrored = true; - - logger.error("Dev Companion Error:", e); - - showNotification({ - title: "Dev Companion Error", - body: (e as ErrorEvent).message || "No Error Message", - color: "var(--status-danger, red)", - noPersist: true - }); - }); - - ws.addEventListener("close", e => { - if (!wasConnected || hasErrored) return; - - logger.info("Dev Companion Disconnected:", e.code, e.reason); - - showNotification({ - title: "Dev Companion Disconnected", - body: e.reason || "No Reason provided", - color: "var(--status-danger, red)", - noPersist: true, - onClick() { - setTimeout(() => { - socket?.close(1000, "Reconnecting"); - initWs(true); - }, 2500); - } - }); - }); - - ws.addEventListener("message", e => { - try { - var { nonce, type, data } = JSON.parse(e.data); - } catch (err) { - logger.error("Invalid JSON:", err, "\n" + e.data); - return; - } - function reply(error?: string) { - const data = { nonce, ok: !error } as Record; - if (error) data.error = error; - - ws.send(JSON.stringify(data)); - } - function replyData(data: T) { - data.nonce = nonce; - ws.send(JSON.stringify(data)); - } - - logger.info("Received Message:", type, "\n", data); - - switch (type) { - case "diff": { - try { - const { extractType, idOrSearch } = data; - switch (extractType) { - case "id": { - if (typeof idOrSearch !== "number") - throw new Error("Id is not a number, got :" + typeof idOrSearch); - replyData({ - type: "diff", - ok: true, - data: { - patched: extractOrThrow(idOrSearch), - source: extractModule(idOrSearch, false) - }, - moduleNumber: idOrSearch - }); - break; - } - case "search": { - let moduleId; - if (data.findType === FindType.STRING) - moduleId = +findModuleId([idOrSearch.toString()]); - else - moduleId = +findModuleId(mkRegexFind(idOrSearch)); - const p = extractOrThrow(moduleId); - const p2 = extractModule(moduleId, false); - console.log(p, p2, "done"); - replyData({ - type: "diff", - ok: true, - data: { - patched: p, - source: p2 - }, - moduleNumber: moduleId - }); - break; - } - } - } catch (error) { - reply(String(error)); - } - break; - } - case "reload": { - window.location.reload(); - break; - } - case "extract": { - try { - const { extractType, idOrSearch } = data; - switch (extractType) { - case "id": { - if (typeof idOrSearch !== "number") - throw new Error("Id is not a number, got :" + typeof idOrSearch); - else - replyData({ - type: "extract", - ok: true, - data: extractModule(idOrSearch), - moduleNumber: idOrSearch - }); - - break; - } - case "search": { - let moduleId; - if (data.findType === FindType.STRING) - moduleId = +findModuleId([idOrSearch.toString()]); - else - moduleId = +findModuleId(mkRegexFind(idOrSearch)); - replyData({ - type: "extract", - ok: true, - data: extractModule(moduleId), - moduleNumber: moduleId - }); - break; - } - case "find": { - const { findType, findArgs } = data; - try { - var parsedArgs = findArgs.map(parseNode); - } catch (err) { - return reply("Failed to parse args: " + err); - } - - try { - let results: any[]; - switch (findType.replace("find", "").replace("Lazy", "")) { - case "": - results = findAll(parsedArgs[0]); - break; - case "ByProps": - results = findAll(filters.byProps(...parsedArgs)); - break; - case "Store": - results = findAll(filters.byStoreName(parsedArgs[0])); - break; - case "ByCode": - results = findAll(filters.byCode(...parsedArgs)); - break; - case "ModuleId": - results = Object.keys(search(parsedArgs[0])); - break; - case "ComponentByCode": - results = findAll(filters.componentByCode(...parsedArgs)); - break; - default: - return reply("Unknown Find Type " + findType); - } - - const uniqueResultsCount = new Set(results).size; - if (uniqueResultsCount === 0) throw "No results"; - if (uniqueResultsCount > 1) throw "Found more than one result! Make this filter more specific"; - // best name ever - const foundFind: string = [...results][0].toString(); - replyData({ - type: "extract", - ok: true, - find: true, - data: foundFind, - moduleNumber: +findModuleId([foundFind]) - }); - } catch (err) { - return reply("Failed to find: " + err); - } - break; - } - default: - reply(`Unknown Extract type. Got: ${extractType}`); - break; - } - } catch (error) { - reply(String(error)); - } - break; - } - case "testPatch": { - const { find, replacement } = data as PatchData; - - let candidates; - if (data.findType === FindType.STRING) - candidates = search(find.toString()); - else - candidates = search(...mkRegexFind(find)); - - // const candidates = search(find); - const keys = Object.keys(candidates); - if (keys.length !== 1) - return reply("Expected exactly one 'find' matches, found " + keys.length); - - const mod = candidates[keys[0]]; - let src = String(mod.original ?? mod).replaceAll("\n", ""); - - if (src.startsWith("function(")) { - src = "0," + src; - } - - let i = 0; - - for (const { match, replace } of replacement) { - i++; - - try { - const matcher = canonicalizeMatch(parseNode(match)); - const replacement = canonicalizeReplace(parseNode(replace), "PlaceHolderPluginName"); - - const newSource = src.replace(matcher, replacement as string); - - if (src === newSource) throw "Had no effect"; - Function(newSource); - - src = newSource; - } catch (err) { - return reply(`Replacement ${i} failed: ${err}`); - } - } - - reply(); - break; - } - case "testFind": { - const { type, args } = data as FindData; - let parsedArgs; - try { - parsedArgs = args.map(parseNode); - } catch (err) { - return reply("Failed to parse args: " + err); - } - - try { - let results: any[]; - switch (type.replace("find", "").replace("Lazy", "")) { - case "": - results = findAll(parsedArgs[0]); - break; - case "ByProps": - results = findAll(filters.byProps(...parsedArgs)); - break; - case "Store": - results = findAll(filters.byStoreName(parsedArgs[0])); - break; - case "ByCode": - results = findAll(filters.byCode(...parsedArgs)); - break; - case "ModuleId": - results = Object.keys(search(parsedArgs[0])); - break; - case "ComponentByCode": - results = findAll(filters.componentByCode(...parsedArgs)); - break; - default: - return reply("Unknown Find Type " + type); - } - - const uniqueResultsCount = new Set(results).size; - if (uniqueResultsCount === 0) throw "No results"; - if (uniqueResultsCount > 1) throw "Found more than one result! Make this filter more specific"; - } catch (err) { - return reply("Failed to find: " + err); - } - - reply(); - break; - } - default: - reply("Unknown Type " + type); - break; - } - }); -} - export default definePlugin({ name: "DevCompanion", description: "Dev Companion Plugin", authors: [Devs.Ven, Devs.sadan], - reporterTestable: ReporterTestable.None, + // we need to be reporter testable to run and give the reuslts + reporterTestable: IS_COMPANION_TEST ? undefined : ReporterTestable.None, settings, toolboxActions: { @@ -387,12 +58,11 @@ export default definePlugin({ }, start() { + console.log(123); + console.log(reporterData); initWs(); }, - stop() { - socket?.close(1000, "Plugin Stopped"); - socket = void 0; - } + stop: stopWs, }); diff --git a/src/plugins/devCompanion.dev/initWs.tsx b/src/plugins/devCompanion.dev/initWs.tsx new file mode 100644 index 00000000..ca16ee16 --- /dev/null +++ b/src/plugins/devCompanion.dev/initWs.tsx @@ -0,0 +1,350 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { showNotification } from "@api/Notifications"; +import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; +import { filters, findAll, search, wreq } from "@webpack"; +import { reporterData } from "debug/reporterData"; + +import { logger, PORT, settings } from "."; +import { extractModule, extractOrThrow, FindData, findModuleId, FindType, mkRegexFind, parseNode, PatchData, SendData } from "./util"; + +export function stopWs() { + socket?.close(1000, "Plugin Stopped"); + socket = void 0; +} + +export let socket: WebSocket | undefined; + +export function initWs(isManual = false) { + let wasConnected = isManual; + let hasErrored = false; + const ws = socket = new WebSocket(`ws://localhost:${PORT}`); + + function replyData(data: T) { + ws.send(JSON.stringify(data)); + } + + ws.addEventListener("open", () => { + wasConnected = true; + + logger.info("Connected to WebSocket"); + + // send module cache to vscode + replyData({ + type: "moduleList", + data: Object.keys(wreq.m), + ok: true, + }); + // if we are running the reporter with companion integration, send the list to vscode as soon as we can + if (IS_COMPANION_TEST) { + replyData({ + type: "report", + data: reporterData, + ok: true + }); + } + + (settings.store.notifyOnAutoConnect || isManual) && showNotification({ + title: "Dev Companion Connected", + body: "Connected to WebSocket", + noPersist: true + }); + }); + + ws.addEventListener("error", e => { + if (!wasConnected) return; + + hasErrored = true; + + logger.error("Dev Companion Error:", e); + + showNotification({ + title: "Dev Companion Error", + body: (e as ErrorEvent).message || "No Error Message", + color: "var(--status-danger, red)", + noPersist: true + }); + }); + + ws.addEventListener("close", e => { + if (!wasConnected || hasErrored) return; + + logger.info("Dev Companion Disconnected:", e.code, e.reason); + + showNotification({ + title: "Dev Companion Disconnected", + body: e.reason || "No Reason provided", + color: "var(--status-danger, red)", + noPersist: true, + onClick() { + setTimeout(() => { + socket?.close(1000, "Reconnecting"); + initWs(true); + }, 2500); + } + }); + }); + + ws.addEventListener("message", e => { + try { + var { nonce, type, data } = JSON.parse(e.data); + } catch (err) { + logger.error("Invalid JSON:", err, "\n" + e.data); + return; + } + function reply(error?: string) { + const data = { nonce, ok: !error } as Record; + if (error) data.error = error; + + ws.send(JSON.stringify(data)); + } + function replyData(data: T) { + data.nonce = nonce; + ws.send(JSON.stringify(data)); + } + + logger.info("Received Message:", type, "\n", data); + + switch (type) { + case "diff": { + try { + const { extractType, idOrSearch } = data; + switch (extractType) { + case "id": { + if (typeof idOrSearch !== "number") + throw new Error("Id is not a number, got :" + typeof idOrSearch); + replyData({ + type: "diff", + ok: true, + data: { + patched: extractOrThrow(idOrSearch), + source: extractModule(idOrSearch, false) + }, + moduleNumber: idOrSearch + }); + break; + } + case "search": { + let moduleId; + if (data.findType === FindType.STRING) + moduleId = +findModuleId([idOrSearch.toString()]); + + else + moduleId = +findModuleId(mkRegexFind(idOrSearch)); + const p = extractOrThrow(moduleId); + const p2 = extractModule(moduleId, false); + console.log(p, p2, "done"); + replyData({ + type: "diff", + ok: true, + data: { + patched: p, + source: p2 + }, + moduleNumber: moduleId + }); + break; + } + } + } catch (error) { + reply(String(error)); + } + break; + } + case "reload": { + reply(); + window.location.reload(); + break; + } + case "extract": { + try { + const { extractType, idOrSearch } = data; + switch (extractType) { + case "id": { + if (typeof idOrSearch !== "number") + throw new Error("Id is not a number, got :" + typeof idOrSearch); + + else + replyData({ + type: "extract", + ok: true, + data: extractModule(idOrSearch), + moduleNumber: idOrSearch + }); + + break; + } + case "search": { + let moduleId; + if (data.findType === FindType.STRING) + moduleId = +findModuleId([idOrSearch.toString()]); + + else + moduleId = +findModuleId(mkRegexFind(idOrSearch)); + replyData({ + type: "extract", + ok: true, + data: extractModule(moduleId), + moduleNumber: moduleId + }); + break; + } + case "find": { + const { findType, findArgs } = data; + try { + var parsedArgs = findArgs.map(parseNode); + } catch (err) { + return reply("Failed to parse args: " + err); + } + + try { + let results: any[]; + switch (findType.replace("find", "").replace("Lazy", "")) { + case "": + results = findAll(parsedArgs[0]); + break; + case "ByProps": + results = findAll(filters.byProps(...parsedArgs)); + break; + case "Store": + results = findAll(filters.byStoreName(parsedArgs[0])); + break; + case "ByCode": + results = findAll(filters.byCode(...parsedArgs)); + break; + case "ModuleId": + results = Object.keys(search(parsedArgs[0])); + break; + case "ComponentByCode": + results = findAll(filters.componentByCode(...parsedArgs)); + break; + default: + return reply("Unknown Find Type " + findType); + } + + const uniqueResultsCount = new Set(results).size; + if (uniqueResultsCount === 0) throw "No results"; + if (uniqueResultsCount > 1) throw "Found more than one result! Make this filter more specific"; + // best name ever + const foundFind: string = [...results][0].toString(); + replyData({ + type: "extract", + ok: true, + find: true, + data: foundFind, + moduleNumber: +findModuleId([foundFind]) + }); + } catch (err) { + return reply("Failed to find: " + err); + } + break; + } + default: + reply(`Unknown Extract type. Got: ${extractType}`); + break; + } + } catch (error) { + reply(String(error)); + } + break; + } + case "testPatch": { + const { find, replacement } = data as PatchData; + + let candidates; + if (data.findType === FindType.STRING) + candidates = search(find.toString()); + + else + candidates = search(...mkRegexFind(find)); + + // const candidates = search(find); + const keys = Object.keys(candidates); + if (keys.length !== 1) + return reply("Expected exactly one 'find' matches, found " + keys.length); + + const mod = candidates[keys[0]]; + let src = String(mod.original ?? mod).replaceAll("\n", ""); + + if (src.startsWith("function(")) { + src = "0," + src; + } + + let i = 0; + + for (const { match, replace } of replacement) { + i++; + + try { + const matcher = canonicalizeMatch(parseNode(match)); + const replacement = canonicalizeReplace(parseNode(replace), "PlaceHolderPluginName"); + + const newSource = src.replace(matcher, replacement as string); + + if (src === newSource) throw "Had no effect"; + Function(newSource); + + src = newSource; + } catch (err) { + return reply(`Replacement ${i} failed: ${err}`); + } + } + + reply(); + break; + } + case "testFind": { + const { type, args } = data as FindData; + let parsedArgs; + try { + parsedArgs = args.map(parseNode); + } catch (err) { + return reply("Failed to parse args: " + err); + } + + try { + let results: any[]; + switch (type.replace("find", "").replace("Lazy", "")) { + case "": + results = findAll(parsedArgs[0]); + break; + case "ByProps": + results = findAll(filters.byProps(...parsedArgs)); + break; + case "Store": + results = findAll(filters.byStoreName(parsedArgs[0])); + break; + case "ByCode": + results = findAll(filters.byCode(...parsedArgs)); + break; + case "ModuleId": + results = Object.keys(search(parsedArgs[0])); + break; + case "ComponentByCode": + results = findAll(filters.componentByCode(...parsedArgs)); + break; + default: + return reply("Unknown Find Type " + type); + } + + const uniqueResultsCount = new Set(results).size; + if (uniqueResultsCount === 0) throw "No results"; + if (uniqueResultsCount > 1) throw "Found more than one result! Make this filter more specific"; + } catch (err) { + return reply("Failed to find: " + err); + } + + reply(); + break; + } + default: + reply("Unknown Type " + type); + break; + } + }); +} + From 27942dc828e84625a66decc3d10ead04268f8c9b Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Thu, 22 Aug 2024 01:43:39 -0400 Subject: [PATCH 12/22] fix reporter data not being serializable --- src/plugins/devCompanion.dev/initWs.tsx | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/plugins/devCompanion.dev/initWs.tsx b/src/plugins/devCompanion.dev/initWs.tsx index ca16ee16..000c79b1 100644 --- a/src/plugins/devCompanion.dev/initWs.tsx +++ b/src/plugins/devCompanion.dev/initWs.tsx @@ -6,6 +6,7 @@ import { showNotification } from "@api/Notifications"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; +import { Patch } from "@utils/types"; import { filters, findAll, search, wreq } from "@webpack"; import { reporterData } from "debug/reporterData"; @@ -41,13 +42,30 @@ export function initWs(isManual = false) { }); // if we are running the reporter with companion integration, send the list to vscode as soon as we can if (IS_COMPANION_TEST) { + const toSend = reporterData; + for (const i in toSend.failedPatches) { + for (const j in toSend.failedPatches[i]) { + const patch: Patch = toSend.failedPatches[i][j]; + if (patch.find instanceof RegExp) + patch.find = String(patch.find); + if (!Array.isArray(patch.replacement)) + patch.replacement = [patch.replacement]; + patch.replacement = patch.replacement.map(v => { + return { + match: String(v.match), + replace: String(v.replace) + }; + }); + } + } replyData({ type: "report", - data: reporterData, + data: toSend, ok: true }); } + (settings.store.notifyOnAutoConnect || isManual) && showNotification({ title: "Dev Companion Connected", body: "Connected to WebSocket", From c365c86a49e64de3878ea0e81a65ce090f5e6906 Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Fri, 23 Aug 2024 13:25:13 -0400 Subject: [PATCH 13/22] fix serialization and race cond --- src/debug/runReporter.ts | 5 +++- src/plugins/devCompanion.dev/index.tsx | 7 +++--- src/plugins/devCompanion.dev/initWs.tsx | 33 +++++++++---------------- 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index 2c6563b6..70a244f4 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -7,6 +7,7 @@ import { Logger } from "@utils/Logger"; import * as Webpack from "@webpack"; import { patches } from "plugins"; +import { initWs } from "plugins/devCompanion.dev/initWs"; import { loadLazyChunks } from "./loadLazyChunks"; import { reporterData } from "./reporterData"; @@ -78,11 +79,13 @@ async function runReporter() { } } + // if we are running the reporter with companion integration, send the list to vscode as soon as we can + if(IS_COMPANION_TEST) + initWs(); ReporterLogger.log("Finished test"); } catch (e) { ReporterLogger.log("A fatal error occurred:", e); } - console.log(reporterData); } // imported in webpack for reporterData, wrap to avoid running reporter diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index 6e9a287d..22a10dba 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -20,7 +20,6 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; -import { reporterData } from "debug/reporterData"; import { initWs, socket, stopWs } from "./initWs"; console.log("imported"); @@ -58,9 +57,9 @@ export default definePlugin({ }, start() { - console.log(123); - console.log(reporterData); - initWs(); + // if were running the reporter, we need to initws in the reporter file to avoid a race condition + if (!IS_COMPANION_TEST) + initWs(); }, stop: stopWs, diff --git a/src/plugins/devCompanion.dev/initWs.tsx b/src/plugins/devCompanion.dev/initWs.tsx index 000c79b1..a99c168c 100644 --- a/src/plugins/devCompanion.dev/initWs.tsx +++ b/src/plugins/devCompanion.dev/initWs.tsx @@ -6,7 +6,6 @@ import { showNotification } from "@api/Notifications"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; -import { Patch } from "@utils/types"; import { filters, findAll, search, wreq } from "@webpack"; import { reporterData } from "debug/reporterData"; @@ -40,29 +39,19 @@ export function initWs(isManual = false) { data: Object.keys(wreq.m), ok: true, }); - // if we are running the reporter with companion integration, send the list to vscode as soon as we can + if (IS_COMPANION_TEST) { - const toSend = reporterData; - for (const i in toSend.failedPatches) { - for (const j in toSend.failedPatches[i]) { - const patch: Patch = toSend.failedPatches[i][j]; - if (patch.find instanceof RegExp) - patch.find = String(patch.find); - if (!Array.isArray(patch.replacement)) - patch.replacement = [patch.replacement]; - patch.replacement = patch.replacement.map(v => { - return { - match: String(v.match), - replace: String(v.replace) - }; - }); - } - } - replyData({ - type: "report", - data: toSend, - ok: true + const toSend = JSON.stringify(reporterData, (_k, v) => { + if (v instanceof RegExp) + return String(v); + return v; }); + + socket?.send(JSON.stringify({ + type: "report", + data: JSON.parse(toSend), + ok: true + })); } From 58513552487813c668ed3bf23d8271e59d17b56c Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Tue, 27 Aug 2024 17:02:04 -0400 Subject: [PATCH 14/22] fix formatting --- src/plugins/devCompanion.dev/index.tsx | 4 ++-- src/plugins/devCompanion.dev/util.tsx | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index 22a10dba..3c1ccd52 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -36,15 +36,15 @@ export const settings = definePluginSettings({ }, usePatchedModule: { description: "On extract requests, reply with the current patched module (if it is patched) instead of the original", - default: true, type: OptionType.BOOLEAN, + default: true, } }); export default definePlugin({ name: "DevCompanion", description: "Dev Companion Plugin", - authors: [Devs.Ven, Devs.sadan], + authors: [Devs.Ven, Devs.sadan, Devs.Samwich], // we need to be reporter testable to run and give the reuslts reporterTestable: IS_COMPANION_TEST ? undefined : ReporterTestable.None, settings, diff --git a/src/plugins/devCompanion.dev/util.tsx b/src/plugins/devCompanion.dev/util.tsx index a623f131..47e25ce6 100644 --- a/src/plugins/devCompanion.dev/util.tsx +++ b/src/plugins/devCompanion.dev/util.tsx @@ -69,7 +69,8 @@ export function extractModule(id: number, patched = settings.store.usePatchedMod if (!module) throw new Error("No module found for module id:" + id); return patched ? module.$$vencordPatchedSource ?? module.original.toString() : module.original.toString(); -} export function parseNode(node: Node) { +} +export function parseNode(node: Node) { switch (node.type) { case "string": return node.value; From b6baaf81b10aa0234290a56b3dc6b4c43dfa1d17 Mon Sep 17 00:00:00 2001 From: Sqaaakoi Date: Wed, 28 Aug 2024 07:24:56 +1200 Subject: [PATCH 15/22] DevCompanion: opinionated hacky changes --- src/plugins/devCompanion.dev/initWs.tsx | 2 ++ src/plugins/devCompanion.dev/util.tsx | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/devCompanion.dev/initWs.tsx b/src/plugins/devCompanion.dev/initWs.tsx index a99c168c..5dcef2eb 100644 --- a/src/plugins/devCompanion.dev/initWs.tsx +++ b/src/plugins/devCompanion.dev/initWs.tsx @@ -212,6 +212,7 @@ export function initWs(isManual = false) { let results: any[]; switch (findType.replace("find", "").replace("Lazy", "")) { case "": + case "Component": results = findAll(parsedArgs[0]); break; case "ByProps": @@ -317,6 +318,7 @@ export function initWs(isManual = false) { let results: any[]; switch (type.replace("find", "").replace("Lazy", "")) { case "": + case "Component": results = findAll(parsedArgs[0]); break; case "ByProps": diff --git a/src/plugins/devCompanion.dev/util.tsx b/src/plugins/devCompanion.dev/util.tsx index 47e25ce6..61deb2a3 100644 --- a/src/plugins/devCompanion.dev/util.tsx +++ b/src/plugins/devCompanion.dev/util.tsx @@ -95,7 +95,7 @@ export function findModuleId(find: CodeFilter) { throw new Error("No Matches Found"); } if (matches.length !== 1) { - throw new Error("More than one match"); + throw new Error(`This filter matches ${matches.length} modules. Make it more specific!`); } return matches[0]; } From eaa307211087691aca39c8f16eec7429c87047ec Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Wed, 28 Aug 2024 01:35:47 -0400 Subject: [PATCH 16/22] add function to the reporter GUI --- src/debug/reporterData.ts | 6 +- src/plugins/devCompanion.dev/index.tsx | 12 ++-- src/plugins/devCompanion.dev/initWs.tsx | 10 ++- src/plugins/devCompanion.dev/util.tsx | 96 ++++++++++++++++++++++++- src/webpack/patchWebpack.ts | 2 + 5 files changed, 118 insertions(+), 8 deletions(-) diff --git a/src/debug/reporterData.ts b/src/debug/reporterData.ts index 09cd318d..1ed7a9cb 100644 --- a/src/debug/reporterData.ts +++ b/src/debug/reporterData.ts @@ -13,12 +13,16 @@ import { TypeWebpackSearchHistory } from "@webpack"; interface EvaledPatch extends Patch { id: number | string; } +interface ErroredPatch extends EvaledPatch { + oldModule: string, + newModule: string; +} interface ReporterData { failedPatches: { foundNoModule: Patch[]; hadNoEffect: EvaledPatch[]; undoingPatchGroup: EvaledPatch[]; - erroredPatch: EvaledPatch[]; + erroredPatch: ErroredPatch[]; }; failedWebpack: Record; } diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index 3c1ccd52..988f340d 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -35,9 +35,14 @@ export const settings = definePluginSettings({ default: true }, usePatchedModule: { - description: "On extract requests, reply with the current patched module (if it is patched) instead of the original", - type: OptionType.BOOLEAN, + description: "On extract requests, reply with the current patched module (if it is patched) instead of the original.", default: true, + type: OptionType.BOOLEAN, + }, + reloadAfterToggle: { + description: "Reload after a disable/enable plugin command is recived.", + default: true, + type: OptionType.BOOLEAN } }); @@ -45,8 +50,7 @@ export default definePlugin({ name: "DevCompanion", description: "Dev Companion Plugin", authors: [Devs.Ven, Devs.sadan, Devs.Samwich], - // we need to be reporter testable to run and give the reuslts - reporterTestable: IS_COMPANION_TEST ? undefined : ReporterTestable.None, + reporterTestable: ReporterTestable.None, settings, toolboxActions: { diff --git a/src/plugins/devCompanion.dev/initWs.tsx b/src/plugins/devCompanion.dev/initWs.tsx index 5dcef2eb..3fee2e15 100644 --- a/src/plugins/devCompanion.dev/initWs.tsx +++ b/src/plugins/devCompanion.dev/initWs.tsx @@ -8,9 +8,10 @@ import { showNotification } from "@api/Notifications"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; import { filters, findAll, search, wreq } from "@webpack"; import { reporterData } from "debug/reporterData"; +import { Settings } from "Vencord"; import { logger, PORT, settings } from "."; -import { extractModule, extractOrThrow, FindData, findModuleId, FindType, mkRegexFind, parseNode, PatchData, SendData } from "./util"; +import { extractModule, extractOrThrow, FindData, findModuleId, FindType, mkRegexFind, parseNode, PatchData, SendData, toggleEnabled, } from "./util"; export function stopWs() { socket?.close(1000, "Plugin Stopped"); @@ -117,6 +118,13 @@ export function initWs(isManual = false) { logger.info("Received Message:", type, "\n", data); switch (type) { + case "disable": { + const { enabled, pluginName } = data; + const settings = Settings.plugins[pluginName]; + if (enabled !== settings.enabled) + toggleEnabled(pluginName, reply); + break; + } case "diff": { try { const { extractType, idOrSearch } = data; diff --git a/src/plugins/devCompanion.dev/util.tsx b/src/plugins/devCompanion.dev/util.tsx index 61deb2a3..f18496ab 100644 --- a/src/plugins/devCompanion.dev/util.tsx +++ b/src/plugins/devCompanion.dev/util.tsx @@ -4,13 +4,17 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { showNotice } from "@api/Notices"; +import { Settings } from "@api/Settings"; import { canonicalizeMatch } from "@utils/patches"; import { CodeFilter, stringMatches, wreq } from "@webpack"; +import { Toasts } from "@webpack/common"; -import { settings } from "."; +import { settings as companionSettings } from "."; type Node = StringNode | RegexNode | FunctionNode; + export interface StringNode { type: "string"; value: string; @@ -64,7 +68,7 @@ export function extractOrThrow(id) { * @param id module id * @param patched return the patched module */ -export function extractModule(id: number, patched = settings.store.usePatchedModule): string { +export function extractModule(id: number, patched = companionSettings.store.usePatchedModule): string { const module = wreq.m[id]; if (!module) throw new Error("No module found for module id:" + id); @@ -104,4 +108,92 @@ export function mkRegexFind(idOrSearch: string): RegExp[] { const flags = idOrSearch.substring(idOrSearch.lastIndexOf("/") + 1); return [canonicalizeMatch(RegExp(regex, flags))]; } +// the next two functions are copied from components/pluginSettings +function showErrorToast(message: string) { + Toasts.show({ + message, + type: Toasts.Type.FAILURE, + id: Toasts.genId(), + options: { + position: Toasts.Position.BOTTOM + } + }); +} +export function toggleEnabled(name: string, beforeReload: () => void) { + let restartNeeded = false; + function onRestartNeeded() { + restartNeeded = true; + } + function beforeReturn() { + if (restartNeeded) { + if (companionSettings.store.reloadAfterToggle) { + beforeReload(); + window.location.reload(); + } + Toasts.show({ + id: Toasts.genId(), + message: "Reload Needed", + type: Toasts.Type.MESSAGE, + options: { + duration: 5000, + position: Toasts.Position.TOP + } + }); + } + } + const plugin = Vencord.Plugins.plugins[name]; + + const settings = Settings.plugins[plugin.name]; + + const isEnabled = () => settings.enabled ?? false; + + const wasEnabled = isEnabled(); + + // If we're enabling a plugin, make sure all deps are enabled recursively. + if (!wasEnabled) { + const { restartNeeded, failures } = Vencord.Plugins.startDependenciesRecursive(plugin); + if (failures.length) { + console.error(`Failed to start dependencies for ${plugin.name}: ${failures.join(", ")}`); + showNotice("Failed to start dependencies: " + failures.join(", "), "Close", () => null); + beforeReturn(); + return; + } else if (restartNeeded) { + // If any dependencies have patches, don't start the plugin yet. + settings.enabled = true; + onRestartNeeded(); + beforeReturn(); + return; + } + } + + // if the plugin has patches, dont use stopPlugin/startPlugin. Wait for restart to apply changes. + if (plugin.patches?.length) { + settings.enabled = !wasEnabled; + onRestartNeeded(); + beforeReturn(); + return; + } + + // If the plugin is enabled, but hasn't been started, then we can just toggle it off. + if (wasEnabled && !plugin.started) { + settings.enabled = !wasEnabled; + beforeReturn(); + return; + } + + const result = wasEnabled ? Vencord.Plugins.stopPlugin(plugin) : Vencord.Plugins.startPlugin(plugin); + + if (!result) { + settings.enabled = false; + + const msg = `Error while ${wasEnabled ? "stopping" : "starting"} plugin ${plugin.name}`; + console.error(msg); + showErrorToast(msg); + beforeReturn(); + return; + } + + settings.enabled = !wasEnabled; + beforeReturn(); +} diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 45c6970d..1aee0482 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -318,6 +318,8 @@ function patchFactories(factories: Record Date: Thu, 29 Aug 2024 19:35:17 -0400 Subject: [PATCH 17/22] add readme.md --- src/plugins/devCompanion.dev/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/plugins/devCompanion.dev/README.md diff --git a/src/plugins/devCompanion.dev/README.md b/src/plugins/devCompanion.dev/README.md new file mode 100644 index 00000000..a48e02f8 --- /dev/null +++ b/src/plugins/devCompanion.dev/README.md @@ -0,0 +1,13 @@ +# Dev Companion + +## Features + +- Testing Patches +- Diffing Patches +- Extracting Webpack Modules + - From Patches + - From Finds +- Disable/Enable Plugin buttons above the definePlugin export +- Automatically run the reporter and have a gui with with the results + +## Images/Videos of the Features From ef21be0abbb7df7d6ec10f0e34718dadf732b02a Mon Sep 17 00:00:00 2001 From: sadan4 <117494111+sadan4@users.noreply.github.com> Date: Thu, 29 Aug 2024 20:29:25 -0400 Subject: [PATCH 18/22] add assets to readme.md --- src/plugins/devCompanion.dev/README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/plugins/devCompanion.dev/README.md b/src/plugins/devCompanion.dev/README.md index a48e02f8..2385d11c 100644 --- a/src/plugins/devCompanion.dev/README.md +++ b/src/plugins/devCompanion.dev/README.md @@ -11,3 +11,27 @@ - Automatically run the reporter and have a gui with with the results ## Images/Videos of the Features + +### Reporter Gui + +https://github.com/user-attachments/assets/71c45fda-5161-43b0-8b2d-6e5fae8267d2 + +### Testing Patches + +https://github.com/user-attachments/assets/99a9157e-89bb-45c7-b780-ffac30cdf4d0 + +### Diffing Patches +#### Only works for patches that are currently applied and have not errored +#### Shows every patch to that webpack module, not just yours + +https://github.com/user-attachments/assets/958f4b61-4390-47fa-9dd3-6fc888dc844d + +### Extracting Webpack Modules +#### Use the toggle in the plugin setting to default to the extracted module or the unpatched module if the module is patched + +https://github.com/user-attachments/assets/bbe308c8-af9a-4141-b387-9dcf175cfd25 + +### Disable/Enable Plugins +#### There is a plugin setting to set auto-reload after a plugin is toggled + +https://github.com/user-attachments/assets/56de9c1d-fb6d-4665-aff0-6429f80d1f15 From 5c232f06a667969c2d5239d7c9095efb7d994bec Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Sun, 1 Sep 2024 12:46:21 -0400 Subject: [PATCH 19/22] volumeBooster got merged, removed dupe devs entry --- src/utils/constants.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 6de48596..7fb2b6bf 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -554,10 +554,6 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "Lumap", id: 585278686291427338n, }, - sadan: { - name: "sadan", - id: 521819891141967883n - }, Obsidian: { name: "Obsidian", id: 683171006717755446n, From c7e8f1d7c19011c0777ef09c04001153e3ed028f Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Tue, 17 Sep 2024 17:00:34 -0400 Subject: [PATCH 20/22] guh --- src/plugins/devCompanion.dev/index.tsx | 2 +- src/plugins/devCompanion.dev/initWs.tsx | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index 988f340d..11ab2d4a 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -61,7 +61,7 @@ export default definePlugin({ }, start() { - // if were running the reporter, we need to initws in the reporter file to avoid a race condition + // if we're running the reporter, we need to initws in the reporter file to avoid a race condition if (!IS_COMPANION_TEST) initWs(); }, diff --git a/src/plugins/devCompanion.dev/initWs.tsx b/src/plugins/devCompanion.dev/initWs.tsx index 3fee2e15..89a2aaaa 100644 --- a/src/plugins/devCompanion.dev/initWs.tsx +++ b/src/plugins/devCompanion.dev/initWs.tsx @@ -125,6 +125,15 @@ export function initWs(isManual = false) { toggleEnabled(pluginName, reply); break; } + case "rawId": { + const { id } = data; + replyData({ + ok: true, + data: extractModule(id), + type: "ret" + }); + break; + } case "diff": { try { const { extractType, idOrSearch } = data; From 2f3c9fd8ec3906b1b2c6dbe5e166c18d06195a0c Mon Sep 17 00:00:00 2001 From: sadan4 <117494111+sadan4@users.noreply.github.com> Date: Tue, 24 Sep 2024 23:50:13 -0400 Subject: [PATCH 21/22] Update README.md --- src/plugins/devCompanion.dev/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/devCompanion.dev/README.md b/src/plugins/devCompanion.dev/README.md index 2385d11c..168ec81b 100644 --- a/src/plugins/devCompanion.dev/README.md +++ b/src/plugins/devCompanion.dev/README.md @@ -9,9 +9,14 @@ - From Finds - Disable/Enable Plugin buttons above the definePlugin export - Automatically run the reporter and have a gui with with the results +- Webpack LSP that lets you jump around extracted webpack files ## Images/Videos of the Features +### Webpack LSP + +https://github.com/user-attachments/assets/7d4ab157-0751-4a59-8e0e-1a3607a3247d + ### Reporter Gui https://github.com/user-attachments/assets/71c45fda-5161-43b0-8b2d-6e5fae8267d2 From f73c59b09ef0e7ac12b854423169d1b05a4229c0 Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Sat, 21 Sep 2024 15:11:29 -0400 Subject: [PATCH 22/22] replace notifications with toatsts --- src/plugins/devCompanion.dev/initWs.tsx | 41 +++++++++++++------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/plugins/devCompanion.dev/initWs.tsx b/src/plugins/devCompanion.dev/initWs.tsx index 89a2aaaa..1cb67fa7 100644 --- a/src/plugins/devCompanion.dev/initWs.tsx +++ b/src/plugins/devCompanion.dev/initWs.tsx @@ -4,9 +4,9 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { showNotification } from "@api/Notifications"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; import { filters, findAll, search, wreq } from "@webpack"; +import { Toasts } from "@webpack/common"; import { reporterData } from "debug/reporterData"; import { Settings } from "Vencord"; @@ -56,10 +56,13 @@ export function initWs(isManual = false) { } - (settings.store.notifyOnAutoConnect || isManual) && showNotification({ - title: "Dev Companion Connected", - body: "Connected to WebSocket", - noPersist: true + (settings.store.notifyOnAutoConnect || isManual) && Toasts.show({ + message: "Connected to WebSocket", + id: Toasts.genId(), + type: Toasts.Type.SUCCESS, + options: { + position: Toasts.Position.TOP + } }); }); @@ -70,11 +73,13 @@ export function initWs(isManual = false) { logger.error("Dev Companion Error:", e); - showNotification({ - title: "Dev Companion Error", - body: (e as ErrorEvent).message || "No Error Message", - color: "var(--status-danger, red)", - noPersist: true + Toasts.show({ + message: "Dev Companion Error", + id: Toasts.genId(), + type: Toasts.Type.FAILURE, + options: { + position: Toasts.Position.TOP + } }); }); @@ -83,16 +88,12 @@ export function initWs(isManual = false) { logger.info("Dev Companion Disconnected:", e.code, e.reason); - showNotification({ - title: "Dev Companion Disconnected", - body: e.reason || "No Reason provided", - color: "var(--status-danger, red)", - noPersist: true, - onClick() { - setTimeout(() => { - socket?.close(1000, "Reconnecting"); - initWs(true); - }, 2500); + Toasts.show({ + message: "Dev Companion Disconnected", + id: Toasts.genId(), + type: Toasts.Type.FAILURE, + options: { + position: Toasts.Position.TOP } }); });