mirror of
https://github.com/Equicord/Equicord.git
synced 2025-06-10 07:03:06 -04:00
More progress
This commit is contained in:
parent
1709ab61ef
commit
c39ff8f648
15 changed files with 296 additions and 155 deletions
|
@ -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;
|
7
src/components/Test.tsx
Normal file
7
src/components/Test.tsx
Normal file
|
@ -0,0 +1,7 @@
|
|||
import React from "react";
|
||||
|
||||
export default () => {
|
||||
<div>
|
||||
Hi
|
||||
</div>;
|
||||
};
|
2
src/globals.d.ts
vendored
2
src/globals.d.ts
vendored
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
export default {
|
||||
name: "bar"
|
||||
};
|
||||
import definePlugin from '../utils/types';
|
||||
|
||||
export default definePlugin({
|
||||
name: "bar",
|
||||
description: "Just to test",
|
||||
author: ["Vendicated"],
|
||||
start() {
|
||||
console.log("bar");
|
||||
}
|
||||
});
|
|
@ -1,3 +1,10 @@
|
|||
export default {
|
||||
name: "foo"
|
||||
};
|
||||
import definePlugin from "../utils/types";
|
||||
|
||||
export default definePlugin({
|
||||
name: "foo",
|
||||
description: "Just to test",
|
||||
author: ["Vendicated"],
|
||||
start() {
|
||||
console.log("foo");
|
||||
}
|
||||
});
|
|
@ -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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
2
src/plugins.d.ts → src/pluginsModule.d.ts
vendored
2
src/plugins.d.ts → src/pluginsModule.d.ts
vendored
|
@ -1,4 +1,4 @@
|
|||
declare module "plugins" {
|
||||
var plugins: Record<string, any>[];
|
||||
const plugins: import("./utils/types").Plugin[];
|
||||
export default plugins;
|
||||
}
|
27
src/utils/logger.ts
Normal file
27
src/utils/logger.ts
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<any>();
|
||||
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);
|
||||
|
|
23
src/utils/types.ts
Normal file
23
src/utils/types.ts
Normal file
|
@ -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[];
|
||||
}
|
105
src/utils/webpack.ts
Normal file
105
src/utils/webpack.ts
Normal file
|
@ -0,0 +1,105 @@
|
|||
import { startAll } from "../plugins";
|
||||
import Logger from "./logger";
|
||||
|
||||
let webpackCache: typeof window.webpackChunkdiscord_app;
|
||||
|
||||
export const subscriptions = new Map<FilterFn, CallbackFn>();
|
||||
export const listeners = new Set<CallbackFn>();
|
||||
|
||||
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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue