Skip to content

Commit

Permalink
Add basic support for Vencord conversion target
Browse files Browse the repository at this point in the history
  • Loading branch information
Davilarek committed Jul 25, 2024
1 parent 9b992da commit 8b1b28f
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ dist
src/converter copy.ts
test/sample/betterdiscord/index.js
test/sample/replugged/index.js
test/sample/vencord/index.js
3 changes: 3 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export const IMPLEMENTATION_STORES_PATH_SOURCE = "globalThis";
export const IMPLEMENTATION_STORES_PATH_VAR_NAME = "implementationStores";
export const IMPLEMENTATION_STORES_PATH_REQ = IMPLEMENTATION_STORES_PATH_VAR_NAME + "_require";
export const BV_PATCHER_COMMIT_HASH = "4f566d27074995882422ce3874236a514890263a";
export const BV_PATCHER_SOURCE_URL = (commitHash: string) => `https://github.com/Davilarek/Vencord/raw/${commitHash}/src/plugins/bdCompatLayer/stuffFromBD.js`;
export const VENCORD_PATCHER_GLOBAL_NAME = "VencordSharedPatcher";
2 changes: 1 addition & 1 deletion src/converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,5 +225,5 @@ export default async function (ast: ParseResult<File>, targetedDiscordModApiLibr
parsedBodyWithoutOurImports.unshift(...await addCode(targetedDiscordModApiLibrary.default));
if ((targetedDiscordModApiLibrary as { default: IModImplementation } & { convertFormat: (ast_: Statement[]) => Statement[] }).convertFormat == undefined)
return parsedBodyWithoutOurImports;
return (targetedDiscordModApiLibrary as { default: IModImplementation } & { convertFormat: (ast_: Statement[]) => Statement[] }).convertFormat(parsedBodyWithoutOurImports);
return await (targetedDiscordModApiLibrary as { default: IModImplementation } & { convertFormat: (ast_: Statement[]) => Statement[] }).convertFormat(parsedBodyWithoutOurImports);
}
119 changes: 119 additions & 0 deletions src/converters/vencord.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { IBaseWebpackApi } from "../api/Webpack.js";
import { parse } from "@babel/parser";
import { IBasePatcherApi } from "../api/Patcher.js";
import { IModImplementation } from "../api/ModImplementation.js";
import { createFunctionFromObjectProperty, createFunctionWithWrapperNeeded } from "../api/RuntimeGenerators.js";
import { addComment, assignmentExpression, ClassDeclaration, classExpression, emptyStatement, ExportDefaultDeclaration, identifier, isExportDefaultDeclaration, logicalExpression, memberExpression, removeComments, Statement, toExpression, toStatement, traverse, variableDeclaration, variableDeclarator } from "@babel/types";
import { BV_PATCHER_COMMIT_HASH, BV_PATCHER_SOURCE_URL, VENCORD_PATCHER_GLOBAL_NAME } from "../constants.js";

class VCWebpackApi implements IBaseWebpackApi {
get getModule() {
return createFunctionFromObjectProperty("Vencord.Webpack", "find");
// return undefined as unknown as any;
// return createFunctionThatIsMissing();
// return { wrapperName: "getModule" } as any;
}
}

class VCPatcherApi implements IBasePatcherApi {
get constructor_() {
return createFunctionWithWrapperNeeded(VENCORD_PATCHER_GLOBAL_NAME, "undefined", "Patcher_constructor") as any;
}
// constructor() {
// return this.constructor_;
// }
internalId: undefined;
get unpatchAll() {
return createFunctionWithWrapperNeeded(VENCORD_PATCHER_GLOBAL_NAME, "unpatchAll", "unpatchAllWrapper");
}
get after() {
return createFunctionWithWrapperNeeded(VENCORD_PATCHER_GLOBAL_NAME, "after", "afterWrapper");
}
get before() {
return createFunctionWithWrapperNeeded(VENCORD_PATCHER_GLOBAL_NAME, "before", "beforeWrapper");
}
get instead() {
return createFunctionWithWrapperNeeded(VENCORD_PATCHER_GLOBAL_NAME, "instead", "insteadWrapper");
}
}

export async function convertFormat(ast: Statement[]) {
const response = await fetch(BV_PATCHER_SOURCE_URL(BV_PATCHER_COMMIT_HASH));
const parsed = parse((await response.text()).replace("BdApi.Webpack.findByUniqueProperties", "Vencord.Webpack.findByProps"), { sourceType: "module" }).program.body;
const patcherClass = parsed.find(x => x.type == "ClassDeclaration" && x.id?.name == "Patcher") as ClassDeclaration;
if (!patcherClass)
return ast;
const originalComment = patcherClass.leadingComments;
traverse(patcherClass, {
enter(node) {
removeComments(node);
},
});
// patcherClass.leadingComments = originalComment;
const memberExpr = memberExpression(identifier("window"), identifier(VENCORD_PATCHER_GLOBAL_NAME));
const assigExpr = assignmentExpression("=", memberExpr, logicalExpression("??", memberExpr, classExpression(null, null, patcherClass.body)));
assigExpr.leadingComments = originalComment;
ast.unshift(toStatement(assigExpr));
ast.unshift(addComment(emptyStatement(), "leading", " eslint-disable simple-header/header "));

for (let i = 0; i < ast.length; i++) {
if (isExportDefaultDeclaration(ast[i])) {
const exportDefault = ast[i] as ExportDefaultDeclaration;
const declaration = exportDefault.declaration as ClassDeclaration;
const variableName = 'defaultExport';
const varDeclarator = variableDeclarator(identifier(variableName), toExpression(declaration));
const varDeclaration = variableDeclaration('const', [varDeclarator]);
ast[i] = varDeclaration;
const pluginTemplateCode = `
import definePlugin from "@utils/types";
import { wreq, beforeInitListeners } from "@webpack";
if (Array.prototype.find.call([...beforeInitListeners], x => x && x.toString().includes("writeable")) === undefined) {
beforeInitListeners.add(() => {
console.log("Making exports writeable...");
if (wreq.d.toString().includes("set:"))
{
console.log("Already writeable");
return;
}
wreq.d = (target, exports) => {
for (const key in exports) {
if (!Reflect.has(exports, key)) continue;
Object.defineProperty(target, key, {
get: () => exports[key](),
set: v => { exports[key] = () => v; },
enumerable: true,
configurable: false
});
}
};
});
}
const thePlugin = {
name: "${declaration.id?.name}",
description: "${declaration.id?.name} plugin",
authors: [
{
id: 0n,
name: "Anonymous"
}
],
instance: (() => new ${variableName}())(),
start() {
this.instance.start();
},
stop() {
this.instance.stop();
},
};
export default definePlugin(thePlugin);`;
ast.push(...parse(pluginTemplateCode, { sourceType: "module" }).program.body);
break;
}
}
return ast;
}

export default {
WebpackApi: new VCWebpackApi(),
PatcherApi: VCPatcherApi.prototype,
} as IModImplementation;

0 comments on commit 8b1b28f

Please sign in to comment.