Skip to content

Commit

Permalink
Add some garbage code
Browse files Browse the repository at this point in the history
  • Loading branch information
Davilarek committed Feb 15, 2024
1 parent 7c0eb45 commit fe22fa1
Show file tree
Hide file tree
Showing 9 changed files with 288 additions and 10 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,8 @@
"@typescript-eslint/parser": "^6.20.0",
"eslint": "^8.56.0",
"typescript": "^5.3.3"
},
"dependencies": {
"esserializer": "^1.3.11"
}
}
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 48 additions & 1 deletion src/api/RuntimeGenerators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function createFunctionFromPath(path: string) {
* @returns A function that returns the value of the specified property from the object.
*/
export function createFunctionFromObjectProperty(objectName: string, property: string) {
const generatedFunction: () => any = new Function(`return () => ${objectName}.${property}`)();
const generatedFunction: (...args: any[]) => any = new Function(`return () => ${objectName}.${property}`)();
Object.defineProperty(generatedFunction, "object", {
value: objectName,
});
Expand All @@ -27,3 +27,50 @@ export function createFunctionFromObjectProperty(objectName: string, property: s
});
return generatedFunction;
}

import { FunctionImplementation, implementationStores, initStores } from "../common/index.js";
import { createJavaScriptFromObject } from "../utils.js";
import { parse } from "@babel/parser";
/**
* this is really wrong, TODO: fix this piece of... garbage
*/
export async function addCode() {
// let rawCode = "globalThis.implementationStores = {\n"; // TODO: fix, this is awful
// for (const key in implementationStores) {
// if (Object.prototype.hasOwnProperty.call(implementationStores, key)) {
// const element = implementationStores[key].implementationStore;
// rawCode += `\t${key}: {\n`;
// for (const key2 in element) {
// if (Object.prototype.hasOwnProperty.call(element, key2)) {
// }
// }
// }
// }
await initStores();
const constructed: {
[key: string]: {
[key: string]: FunctionImplementation;
// [key: string]: string;
}
// [key: string]: string,
} = {};
for (const key in implementationStores) {
if (Object.prototype.hasOwnProperty.call(implementationStores, key)) {
constructed[key] = {
...implementationStores[key].implementationStore,
};
// constructed[key] = {};
// for (const key2 in implementationStores[key].implementationStore) {
// if (Object.prototype.hasOwnProperty.call(implementationStores[key].implementationStore, key2)) {
// const element = implementationStores[key].implementationStore[key2];
// constructed[key][key2] = getMain(serializer).serialize(element);
// }
// }
}
}
// const rawCode = "globalThis.implementationStores = {\n" + getMain(serializer).serialize(constructed) + "\n}";
const rawCode = "globalThis.implementationStores = (" + createJavaScriptFromObject(constructed, true) + ")";
// console.log(rawCode);
const rawCodeAst = parse(rawCode);
return rawCodeAst.program.body;
}
13 changes: 8 additions & 5 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { readFileSync, readdirSync } from "fs";
import * as url from 'url';
import converter from "./converter.js";
import { File } from "@babel/types";
import { myPackageName } from "./utils.js";
import { getMain, myPackageName } from "./utils.js";
import { transformSync } from "@babel/core";
import { IModImplementation } from "./api/ModImplementation.js";
// import { addCode } from "./api/RuntimeGenerators.js";

if (process.argv.length != 4) {
console.error(`Usage:\n\t${myPackageName} <input file> <target client mod>\nExample:\n\t${myPackageName} ./index.js BetterDiscord`);
Expand Down Expand Up @@ -39,20 +40,22 @@ if (!isClientModSupported) {
console.error("Supported client mods: " + supportedClientMods.join(", "));
process.exit(1);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
// addCode().then(_ => process.exit(0));
// // process.exit(0);
const filler = import(url.pathToFileURL(`${__dirname}/converters/${targetDiscordMod}.js`).href);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
filler.then((x: { default: IModImplementation }) => {
filler.then(async (x: { default: IModImplementation }) => {
if (x.default.importsForbidden)
console.warn('\x1b[33m%s\x1b[0m', `Warning: Target mod ${targetDiscordMod} requires your code to be bundled into single file`);
const out = converter(ast as File & { errors: [] }, x);
const out = await converter(ast as File & { errors: [] }, x);
const outMod = {
...ast,
program: {
...ast.program,
body: out,
},
} as File;
const generate = typeof generate_ == "function" ? generate_ : (generate_ as { default: typeof generate_ }).default;
const generate = getMain(generate_);
console.log("generated code: ", generate(outMod).code);
});
62 changes: 62 additions & 0 deletions src/common/Webpack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { FunctionImplementation, require } from "./index.js";
import { IModImplementation } from "../api/ModImplementation";

// const reservedData = {} as { [key: string]: any };

export let targetMod: IModImplementation;

const implementationStore = {
getModule: new FunctionImplementation({
depends: [],
supplies: "getModule",
// get data() {
// return reservedData[this.supplies];
// },
// set data(v: any) {
// reservedData[this.supplies] = v;
// },
data: {},
func(filter: (mod: any) => boolean) {
if (this.data.req == undefined) {
// @ts-expect-error Non-standard property
window.webpackChunkdiscord_app.push(
[[Symbol()],
{},
(r: { c: any; }) =>
this.data = this.data ?? {
req: r,
},
]);
// @ts-expect-error Non-standard property
window.webpackChunkdiscord_app.pop();
}
// @ts-expect-error too lazy
return Object.values(this.data.req).find(filter)?.exports;
},
}),
getByStrings: new FunctionImplementation({
depends: ["getModule"],
supplies: "getByStrings",
data: null,
func(...strings) {
const getModule = require(targetMod, "Webpack", "getModule");
if (getModule) {
return getModule((module: any) => {
if (!module?.toString || typeof (module?.toString) !== "function") return; // Not stringable
let moduleString = "";
try { moduleString = module?.toString([]); }
catch (err) { moduleString = module?.toString(); }
if (!moduleString) return false; // Could not create string
for (const s of strings) {
if (!moduleString.includes(s)) return false;
}
return true;
});
}
throw new Error("Unimplemented");
},
}),
} as { [key: string]: FunctionImplementation };
export {
implementationStore,
};
67 changes: 67 additions & 0 deletions src/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { IModImplementation } from "../api/ModImplementation";
import { getKeyValue } from "../utils.js";

export interface IFunctionImplementation {
supplies: string,
depends: string[],
data: any,
func: (...args: any[]) => any,
}
class FunctionImplementation implements IFunctionImplementation {
supplies: string;
depends: string[];
data: any;
func: (...args: any[]) => any;
// constructor(supplies: string, depends: string[], data: any, func: (...args: any[]) => any) {
// this.supplies = supplies;
// this.depends = depends;
// this.data = data;
// this.func = func;
// }
constructor(options: IFunctionImplementation) {
const { supplies, depends, data, func } = options;
this.supplies = supplies!;
this.depends = depends!;
this.data = data!;
this.func = func!;
}
}
export {
FunctionImplementation,
};
// import * as WebpackImplementations from "./Webpack.js";
import { createFunctionFromObjectProperty } from "../api/RuntimeGenerators.js";
import { readdirSync } from "fs";
import * as url from 'url';
import * as path from 'path';
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));

export const implementationStores = {
// "Webpack": WebpackImplementations,
} as { [category: string]: { implementationStore: { [key: string]: FunctionImplementation }, targetMod: IModImplementation } };
export async function initStores() {
const stores = readdirSync(`${__dirname}`).filter(x => !x.startsWith("index."));
console.log(stores);
for (let index = 0; index < stores.length; index++) {
const filler = import(url.pathToFileURL(`${__dirname}/${stores[index]}`).href);
const mod = await filler;
implementationStores[path.parse(stores[index]).name] = mod;
}
console.log("done");
}
export function doesImplement(mod: IModImplementation, category: string, method: string) {
const categoryObj = getKeyValue(mod, category as keyof IModImplementation);
return getKeyValue(categoryObj, method as never) == undefined ? false : true;
}

export function require(mod: IModImplementation, category: string, method: string) {
if (doesImplement(mod, category, method)) {
const categoryObj = getKeyValue(mod, category as keyof IModImplementation);
return getKeyValue(categoryObj, method as never);
}
implementationStores[category].targetMod = mod;
const foundImplementation = implementationStores[category].implementationStore[method];
if (foundImplementation == undefined)
return null; // depends failed
return createFunctionFromObjectProperty(`globalThis.implementationStores.${category}`, method);
}
8 changes: 4 additions & 4 deletions src/converter.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { ParseResult } from "@babel/parser";
import { File, Identifier, ImportDeclaration, ImportSpecifier, MemberExpression, Statement } from "@babel/types";
import { NonFunctionType, myPackageName } from "./utils.js";
import { NonFunctionType, getKeyValue, myPackageName } from "./utils.js";
import { IModImplementation } from "./api/ModImplementation";
import { addCode } from "./api/RuntimeGenerators.js";

function removeASTLocation(ast: Statement[] | Statement) {
if (Array.isArray(ast)) {
Expand Down Expand Up @@ -93,9 +94,7 @@ function deepFind<K>(obj: any, path: string): K | undefined {
return current;
}

const getKeyValue = <T, K extends keyof T>(obj: T, key: K): T[K] => obj[key];

export default function (ast: ParseResult<File>, targetedDiscordModApiLibrary: { default: IModImplementation }): Statement[] {
export default async function (ast: ParseResult<File>, targetedDiscordModApiLibrary: { default: IModImplementation }): Promise<Statement[]> {
const parsedBody = ast.program.body;
const importStatements = parsedBody.filter(x => x.type == "ImportDeclaration");
const importsToBake = [];
Expand Down Expand Up @@ -147,5 +146,6 @@ export default function (ast: ParseResult<File>, targetedDiscordModApiLibrary: {
}
}
}
parsedBodyWithoutOurImports.unshift(...await addCode());
return parsedBodyWithoutOurImports;
}
86 changes: 86 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,89 @@ export const myPackageName = (() => {

// eslint-disable-next-line @typescript-eslint/ban-types
export type NonFunctionType<T> = T extends Function ? never : T;
// eslint-disable-next-line @typescript-eslint/ban-types
export type OnlyFunctionType<T> = T extends Function ? T : never;
export const getKeyValue = <T, K extends keyof T>(obj: T, key: K): T[K] => obj[key];
// eslint-disable-next-line @typescript-eslint/ban-types
export const getMain = <T>(t: T | { default: T }) => (typeof t == "function" ? t : (t as { default: T }).default);
export function escapeJsonString(str: string) {
return str.replace(/["\\]/g, '\\$&')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/\t/g, '\\t')
.replace(/\f/g, '\\f');
}
/**
* this function tries to create valid JSON with functions embedded
*/
export function createJavaScriptFromObject(obj_: any, intend = false) {
const tree = [
"{",
];
const tree_Tabs = [0];
function enumerateObject(obj: any, tabOffset = 0, iter = 0) {
if (obj === null) {
return;
}
if (Array.isArray(obj)) {
for (let i = 0; i < obj.length; i++) {
enumerateObject(obj[i], tabOffset, iter + 1);
}
}
else if (typeof obj === 'object') {
for (const key in obj) {
const descriptor = Object.getOwnPropertyDescriptor(obj, key);
if (descriptor && (descriptor.get || descriptor.set)) {
console.log(`Property ${key} is a getter/setter.`);
}
else {
tree.push(key + ": ");
tree_Tabs.push(tabOffset);
if (typeof obj[key] === 'object') {
// console.log(`Object ${key}:`);
const isArray = Array.isArray(obj[key]);
if (isArray) {
tree.push("[");
tree_Tabs.push(tabOffset);
}
else {
tree.push("{");
tree_Tabs.push(tabOffset);
}
enumerateObject(obj[key], tabOffset + 1, iter + 1);
if (isArray) {
tree.push("],");
tree_Tabs.push(tabOffset);
}
else {
tree.push("},");
tree_Tabs.push(tabOffset);
}
}
else {
// console.log(`Key: ${key}, Value: ${obj[key]}`);
// tree.push(`"${obj[key]}",`.replace(/\n/g, "\\n"));
tree.push("\"" + escapeJsonString(`${obj[key]}`) + "\",");
tree_Tabs.push(tabOffset + 1);
}
}
}
}
else {
tree.push(`"${obj}",`.replace(/\n/g, "\\n"));
tree_Tabs.push(tabOffset);
}
}
enumerateObject(obj_, 1);
tree.push("}");
tree_Tabs.push(0);
// console.log(tree);
const final = [...tree];
if (intend) {
final.length = 0;
for (let index = 0; index < tree.length; index++) {
final.push(new Array(tree_Tabs[index]).fill("\t").join("") + tree[index]);
}
}
return final.join("\n");
}
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
"rootDir": "./src",
"strict": true,
// "esModuleInterop": true
"skipLibCheck": true,
}
}

0 comments on commit fe22fa1

Please sign in to comment.