diff --git a/build.mjs b/build.mjs index 6459c63..0993eae 100755 --- a/build.mjs +++ b/build.mjs @@ -68,6 +68,7 @@ await Promise.all([ bundle: true, platform: "node", target: ["esnext"], + sourcemap: "linked", plugins: [makeAllPackagesExternalPlugin], watch }), @@ -79,6 +80,7 @@ await Promise.all([ target: ["esnext"], external: ["electron"], platform: "node", + sourcemap: "linked", plugins: [makeAllPackagesExternalPlugin], watch }), @@ -94,6 +96,7 @@ await Promise.all([ plugins: [ globPlugins ], + sourcemap: "inline", watch }) ]).then(res => { diff --git a/package.json b/package.json index 6592d00..1e9c135 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,8 @@ { "devDependencies": { + "@types/flux": "^3.1.11", "@types/node": "^18.7.13", + "@types/react": "^18.0.17", "electron": "^20.1.0", "esbuild": "^0.15.5" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0bc52ca..ab8208b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,11 +1,12 @@ lockfileVersion: 5.3 specifiers: + '@types/flux': ^3.1.11 '@types/node': ^18.7.13 + '@types/react': ^18.0.17 electron: ^20.1.0 electron-devtools-installer: ^3.2.0 esbuild: ^0.15.5 - esbuild-plugin-import-glob: ^0.1.1 jsposed: ^1.0.2 prettier: ^2.7.1 @@ -15,10 +16,11 @@ dependencies: prettier: 2.7.1 devDependencies: + '@types/flux': 3.1.11 '@types/node': 18.7.13 + '@types/react': 18.0.17 electron: 20.1.0 esbuild: 0.15.5 - esbuild-plugin-import-glob: 0.1.1 packages: @@ -49,27 +51,6 @@ packages: dev: true optional: true - /@nodelib/fs.scandir/2.1.5: - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - dev: true - - /@nodelib/fs.stat/2.0.5: - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - dev: true - - /@nodelib/fs.walk/1.2.8: - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.13.0 - dev: true - /@sindresorhus/is/0.14.0: resolution: {integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==} engines: {node: '>=6'} @@ -82,6 +63,17 @@ packages: defer-to-connect: 1.1.3 dev: true + /@types/fbemitter/2.0.32: + resolution: {integrity: sha512-Hwq28bBlbmfCgLnNJvjl5ssTrbZCTSblI4vqPpqZrbbEL8vn5l2UivxhlMYfUY7a4SR8UB6RKoLjOZfljqAa6g==} + dev: true + + /@types/flux/3.1.11: + resolution: {integrity: sha512-Aq4UB1ZqAKcPbhB0GpgMw2sntvOh71he9tjz53TLKrI7rw3Y3LxCW5pTYY9IV455hQapm4pmxFjpqlWOs308Yg==} + dependencies: + '@types/fbemitter': 2.0.32 + '@types/react': 18.0.17 + dev: true + /@types/node/16.11.56: resolution: {integrity: sha512-aFcUkv7EddxxOa/9f74DINReQ/celqH8DiB3fRYgVDM2Xm5QJL8sl80QKuAnGvwAsMn+H3IFA6WCrQh1CY7m1A==} dev: true @@ -90,6 +82,22 @@ packages: resolution: {integrity: sha512-46yIhxSe5xEaJZXWdIBP7GU4HDTG8/eo0qd9atdiL+lFpA03y8KS+lkTN834TWJj5767GbWv4n/P6efyTFt1Dw==} dev: true + /@types/prop-types/15.7.5: + resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} + dev: true + + /@types/react/18.0.17: + resolution: {integrity: sha512-38ETy4tL+rn4uQQi7mB81G7V1g0u2ryquNmsVIOKUAEIDK+3CUjZ6rSRpdvS99dNBnkLFL83qfmtLacGOTIhwQ==} + dependencies: + '@types/prop-types': 15.7.5 + '@types/scheduler': 0.16.2 + csstype: 3.1.0 + dev: true + + /@types/scheduler/0.16.2: + resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} + dev: true + /@types/yauzl/2.10.0: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true @@ -114,13 +122,6 @@ packages: concat-map: 0.0.1 dev: false - /braces/3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} - engines: {node: '>=8'} - dependencies: - fill-range: 7.0.1 - dev: true - /buffer-crc32/0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} dev: true @@ -160,6 +161,10 @@ packages: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} dev: false + /csstype/3.1.0: + resolution: {integrity: sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==} + dev: true + /debug/4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -389,12 +394,6 @@ packages: dev: true optional: true - /esbuild-plugin-import-glob/0.1.1: - resolution: {integrity: sha512-yAFH+9AoIcsQkODSx0KUPRv1FeJUN6Tef8vkPQMcuVkc2vXYneYKsHhOiFS/yIsg5bQ70HHtAlXVA1uTjgoJXg==} - dependencies: - fast-glob: 3.2.11 - dev: true - /esbuild-sunos-64/0.15.5: resolution: {integrity: sha512-2sIzhMUfLNoD+rdmV6AacilCHSxZIoGAU2oT7XmJ0lXcZWnCvCtObvO6D4puxX9YRE97GodciRGDLBaiC6x1SA==} engines: {node: '>=12'} @@ -480,36 +479,12 @@ packages: - supports-color dev: true - /fast-glob/3.2.11: - resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==} - engines: {node: '>=8.6.0'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.5 - dev: true - - /fastq/1.13.0: - resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} - dependencies: - reusify: 1.0.4 - dev: true - /fd-slicer/1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} dependencies: pend: 1.2.0 dev: true - /fill-range/7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} - engines: {node: '>=8'} - dependencies: - to-regex-range: 5.0.1 - dev: true - /fs-extra/8.1.0: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} @@ -551,13 +526,6 @@ packages: pump: 3.0.0 dev: true - /glob-parent/5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - dependencies: - is-glob: 4.0.3 - dev: true - /glob/7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: @@ -669,23 +637,6 @@ packages: dev: true optional: true - /is-extglob/2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - dev: true - - /is-glob/4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - dependencies: - is-extglob: 2.1.1 - dev: true - - /is-number/7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - dev: true - /isarray/1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} dev: false @@ -759,19 +710,6 @@ packages: dev: true optional: true - /merge2/1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - dev: true - - /micromatch/4.0.5: - resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} - engines: {node: '>=8.6'} - dependencies: - braces: 3.0.2 - picomatch: 2.3.1 - dev: true - /mimic-response/1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} @@ -841,11 +779,6 @@ packages: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} dev: true - /picomatch/2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - dev: true - /pify/3.0.0: resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} engines: {node: '>=4'} @@ -884,10 +817,6 @@ packages: once: 1.4.0 dev: true - /queue-microtask/1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true - /readable-stream/2.3.7: resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} dependencies: @@ -906,11 +835,6 @@ packages: lowercase-keys: 1.0.1 dev: true - /reusify/1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: true - /rimraf/3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true @@ -931,12 +855,6 @@ packages: dev: true optional: true - /run-parallel/1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - dependencies: - queue-microtask: 1.2.3 - dev: true - /safe-buffer/5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} dev: false @@ -995,13 +913,6 @@ packages: engines: {node: '>=6'} dev: true - /to-regex-range/5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - dependencies: - is-number: 7.0.0 - dev: true - /tslib/2.4.0: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} dev: false diff --git a/src/Vencord.ts b/src/Vencord.ts index 449423c..091421e 100644 --- a/src/Vencord.ts +++ b/src/Vencord.ts @@ -1,5 +1,8 @@ +import * as plugins from "./plugins"; +import * as WP from "./utils/webpack"; + import "./utils/patchWebpack"; import "./utils/quickCss"; -export const Webpack = {}; -import "./plugins"; +export const Webpack = WP; +export const Plugins = plugins; \ No newline at end of file diff --git a/src/components/Test.tsx b/src/components/Test.tsx new file mode 100644 index 0000000..14a46dd --- /dev/null +++ b/src/components/Test.tsx @@ -0,0 +1,7 @@ +import React from "react"; + +export default () => { +
+ Hi +
; +}; diff --git a/src/globals.d.ts b/src/globals.d.ts index ca43786..02ea088 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -1,5 +1,6 @@ declare global { export var VencordNative: typeof import("./VencordNative").default; + export var Vencord: typeof import("./Vencord"); export var appSettings: { set(setting: string, v: any): void; }; @@ -7,6 +8,7 @@ declare global { interface Window { webpackChunkdiscord_app: { push(chunk: any): any; + pop(): any; }; } } diff --git a/src/plugins/bar.ts b/src/plugins/bar.ts index d503e8a..76d1a8f 100644 --- a/src/plugins/bar.ts +++ b/src/plugins/bar.ts @@ -1,3 +1,10 @@ -export default { - name: "bar" -}; \ No newline at end of file +import definePlugin from '../utils/types'; + +export default definePlugin({ + name: "bar", + description: "Just to test", + author: ["Vendicated"], + start() { + console.log("bar"); + } +}); \ No newline at end of file diff --git a/src/plugins/foo.ts b/src/plugins/foo.ts index 64c9323..f365b31 100644 --- a/src/plugins/foo.ts +++ b/src/plugins/foo.ts @@ -1,3 +1,10 @@ -export default { - name: "foo" -}; \ No newline at end of file +import definePlugin from "../utils/types"; + +export default definePlugin({ + name: "foo", + description: "Just to test", + author: ["Vendicated"], + start() { + console.log("foo"); + } +}); \ No newline at end of file diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 4e55edb..d5b419b 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -1,3 +1,27 @@ -import plugins from "plugins"; +import Plugins from "plugins"; +import Logger from "../utils/logger"; +import { Patch } from "../utils/types"; -console.log(plugins); \ No newline at end of file +const logger = new Logger("PluginManager", "#a6d189"); + +export const plugins = Plugins; +export const patches = [] as Patch[]; + +for (const plugin of Plugins) if (plugin.patches) { + for (const patch of plugin.patches) { + patch.plugin = plugin.name; + if (!Array.isArray(patch.replacement)) patch.replacement = [patch.replacement]; + patches.push(patch); + } +} + +export function startAll() { + for (const plugin of plugins) if (plugin.start) { + try { + logger.info("Starting plugin", plugin.name); + plugin.start(); + } catch (err) { + logger.error("Failed to start plugin", plugin.name, err); + } + } +} \ No newline at end of file diff --git a/src/plugins.d.ts b/src/pluginsModule.d.ts similarity index 50% rename from src/plugins.d.ts rename to src/pluginsModule.d.ts index e287823..0ef3819 100644 --- a/src/plugins.d.ts +++ b/src/pluginsModule.d.ts @@ -1,4 +1,4 @@ declare module "plugins" { - var plugins: Record[]; + const plugins: import("./utils/types").Plugin[]; export default plugins; } \ No newline at end of file diff --git a/src/utils/logger.ts b/src/utils/logger.ts new file mode 100644 index 0000000..be341ee --- /dev/null +++ b/src/utils/logger.ts @@ -0,0 +1,27 @@ +export default class Logger { + constructor(public name: string, public color: string) { } + + private _log(level: "log" | "error" | "warn" | "info" | "debug", args: any[]) { + console[level](`%c ${this.name} `, `background: ${this.color}; color: black; font-weight: bold`, ...args); + } + + public log(...args: any[]) { + this._log("log", args); + } + + public info(...args: any[]) { + this._log("info", args); + } + + public error(...args: any[]) { + this._log("error", args); + } + + public warn(...args: any[]) { + this._log("warn", args); + } + + public debug(...args: any[]) { + this._log("debug", args); + } +} \ No newline at end of file diff --git a/src/utils/patchWebpack.ts b/src/utils/patchWebpack.ts index 6a379de..0e94694 100644 --- a/src/utils/patchWebpack.ts +++ b/src/utils/patchWebpack.ts @@ -1,7 +1,12 @@ import { WEBPACK_CHUNK } from './constants'; +import Logger from "./logger"; +import { _initWebpack } from "./webpack"; let webpackChunk: any[]; +const logger = new Logger("WebpackInterceptor", "#8caaee"); + + Object.defineProperty(window, WEBPACK_CHUNK, { get: () => webpackChunk, set: (v) => { @@ -10,6 +15,8 @@ Object.defineProperty(window, WEBPACK_CHUNK, { // - Webpack's push with toString result of function() { [native code] } // We don't want to override the native one, so check for "push" if (v && !v.push.toString().includes("push")) { + logger.info(`Patching ${WEBPACK_CHUNK}.push`); + _initWebpack(v); patchPush(); // @ts-ignore delete window[WEBPACK_CHUNK]; @@ -24,8 +31,8 @@ function patchPush() { function handlePush(chunk) { try { const modules = chunk[1]; - const subscriptions = new Set(); - const patches = [] as any[]; + const { subscriptions, listeners } = Vencord.Webpack; + const { patches } = Vencord.Plugins; for (const id in modules) { let mod = modules[id]; @@ -40,10 +47,17 @@ function patchPush() { // Just rethrow discord errors if (mod === originalMod) throw err; - console.error("[Webpack] Error in patched chunk", err); + logger.error("Error in patched chunk", err); return originalMod(module, exports, require); } + for (const callback of listeners) { + try { + callback(exports); + } catch (err) { + logger.error("Error in webpack listener", err); + } + } for (const [filter, callback] of subscriptions) { try { if (filter(exports)) { @@ -54,7 +68,7 @@ function patchPush() { callback(exports.default); } } catch (err) { - console.error("[Webpack] Error while firing callback for webpack chunk", err); + logger.error("Error while firing callback for webpack chunk", err); } } }; @@ -63,25 +77,28 @@ function patchPush() { const patch = patches[i]; if (code.includes(patch.find)) { patchedBy.add(patch.plugin); - const lastMod = mod; - const lastCode = code; - try { - const newCode = code.replace(patch.replacement.match, patch.replacement.replace); - const newMod = (0, eval)(`// Webpack Module ${id} - Patched by ${[...patchedBy].join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${id}`); - code = newCode; - mod = newMod; - patches.splice(i--, 1); - } catch (err) { - console.error("[Webpack] Failed to apply patch of", patch.plugin, err); - code = lastCode; - mod = lastMod; - patchedBy.delete(patch.plugin); + // @ts-ignore we change all patch.replacement to array in plugins/index + for (const replacement of patch.replacement) { + const lastMod = mod; + const lastCode = code; + try { + const newCode = code.replace(replacement.match, replacement.replace); + const newMod = (0, eval)(`// Webpack Module ${id} - Patched by ${[...patchedBy].join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${id}`); + code = newCode; + mod = newMod; + } catch (err) { + logger.error("Failed to apply patch of", patch.plugin, err); + code = lastCode; + mod = lastMod; + patchedBy.delete(patch.plugin); + } } + patches.splice(i--, 1); } } } } catch (err) { - console.error("oopsie", err); + logger.error("oopsie", err); } return handlePush.original.call(window[WEBPACK_CHUNK], chunk); diff --git a/src/utils/types.ts b/src/utils/types.ts new file mode 100644 index 0000000..6268d29 --- /dev/null +++ b/src/utils/types.ts @@ -0,0 +1,23 @@ +// exists to export default definePlugin({...}) +export default function definePlugin(p: Plugin) { + return p; +} + +export interface PatchReplacement { + match: string | RegExp; + replace: string | ((match: string, ...groups: string[]) => string); +} + +export interface Patch { + plugin: string; + find: string, + replacement: PatchReplacement | PatchReplacement[]; +} + +export interface Plugin { + name: string; + description: string; + author: string[]; + start?(): void; + patches?: Patch[]; +} \ No newline at end of file diff --git a/src/utils/webpack.ts b/src/utils/webpack.ts new file mode 100644 index 0000000..3f21106 --- /dev/null +++ b/src/utils/webpack.ts @@ -0,0 +1,105 @@ +import { startAll } from "../plugins"; +import Logger from "./logger"; + +let webpackCache: typeof window.webpackChunkdiscord_app; + +export const subscriptions = new Map(); +export const listeners = new Set(); + +type FilterFn = (mod: any) => boolean; +type CallbackFn = (mod: any) => void; + +export let Common: { + React: typeof import("react"), + FluxDispatcher: any; + UserStore: any; +} = {} as any; + +export function _initWebpack(instance: typeof window.webpackChunkdiscord_app) { + if (webpackCache !== void 0) throw "no."; + + webpackCache = instance.push([[Symbol()], {}, (r) => r.c]); + instance.pop(); + + // Abandon Hope All Ye Who Enter Here + + let started = false; + waitFor("getCurrentUser", x => Common.UserStore = x); + waitFor(["dispatch", "subscribe"], x => { + Common.FluxDispatcher = x; + const cb = () => { + console.info("Connection open"); + x.unsubscribe("CONNECTION_OPEN", cb); + startAll(); + }; + x.subscribe("CONNECTION_OPEN", cb); + }); + waitFor("useState", x => Common.React = x); +} + +export function find(filter: FilterFn, getDefault = true) { + if (typeof filter !== "function") + throw new Error("Invalid filter. Expected a function got", filter); + + for (const key in webpackCache) { + const mod = webpackCache[key]; + if (mod?.exports && filter(mod.exports)) + return mod.exports; + if (mod?.exports?.default && filter(mod.exports.default)) + return getDefault ? mod.exports.default : mod.exports; + } + + return null; +} + +export function findAll(filter: FilterFn, getDefault = true) { + if (typeof filter !== "function") throw new Error("Invalid filter. Expected a function got", filter); + + const ret = [] as any[]; + for (const key in webpackCache) { + const mod = webpackCache[key]; + if (mod?.exports && filter(mod.exports)) ret.push(mod.exports); + if (mod?.exports?.default && filter(mod.exports.default)) ret.push(getDefault ? mod.exports.default : mod.exports); + } + + return ret; +} + +export const filters = { + byProps: (props: string[]): FilterFn => + props.length === 1 + ? m => m[props[0]] !== void 0 + : m => props.every(p => m[p] !== void 0), + byDisplayName: (deezNuts: string): FilterFn => m => m.default?.displayName === deezNuts +}; + +export function findByProps(...props: string[]) { + return find(filters.byProps(props)); +} + +export function findAllByProps(...props: string[]) { + return findAll(filters.byProps(props)); +} + +export function findByDisplayName(deezNuts: string) { + return find(filters.byDisplayName(deezNuts)); +} + +export function waitFor(filter: string | string[] | FilterFn, callback: CallbackFn) { + if (typeof filter === "string") filter = filters.byProps([filter]); + else if (Array.isArray(filter)) filter = filters.byProps(filter); + else if (typeof filter !== "function") throw new Error("filter must be a string, string[] or function, got", filter); + + const existing = find(filter!); + if (existing) return void callback(existing); + + subscriptions.set(filter, callback); +} + +export function addListener(callback: CallbackFn) { + listeners.add(callback); +} + +export function removeListener(callback: CallbackFn) { + listeners.delete(callback); +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index eb472c9..313a717 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,10 @@ "moduleResolution": "node", "strict": true, "noImplicitAny": false, - "target": "ESNEXT" + "target": "ESNEXT", + // https://esbuild.github.io/api/#jsx-factory + "jsxFactory": "Vencord.React.createElement", + "jsx": "react" }, - "include": ["src/**/*.ts"] + "include": ["src/**/*"] }