equicord-companion
Version:
Equicord Companion is a vscode extension to test Equicord patches & webpack finds right from the comfort of your IDE
128 lines (111 loc) • 3.87 kB
text/typescript
import { DisablePluginData } from "@type/server";
import {
createSourceFile,
isCallExpression,
isExportAssignment,
isIdentifier,
isObjectLiteralExpression,
isPropertyAssignment,
isStringLiteral,
Node,
ObjectLiteralExpression,
ScriptTarget,
StringLiteral,
} from "typescript";
import { CodeLens, CodeLensProvider, ProviderResult, Range, TextDocument } from "vscode";
enum ParseResult {
INVALID,
NOT_FOUND,
}
function isSerialziable(node: Node): asserts node is StringLiteral {
if (!isStringLiteral(node))
throw new Error("node is not serializable");
}
function parseObjLiteralExpr<
T extends readonly string[],
>(expr: ObjectLiteralExpression, search: [...T]): { [key in T[number]]: string } {
const toRet: Record<string, string> = {};
for (const node of expr.properties) {
if (!isPropertyAssignment(node))
continue;
if (!isIdentifier(node.name))
continue;
if (!search.includes(node.name.text))
continue;
isSerialziable(node.initializer);
toRet[node.name.text] = node.initializer.text;
}
if (search.some((v) => !toRet[v]))
throw new Error("Not all search values found");
return toRet as any;
}
function parsePossiblePatches(node: Node): {
posStart: number;
posEnd: number;
pluginName: string;
} | ParseResult {
if (!isExportAssignment(node))
return ParseResult.NOT_FOUND;
if (!isCallExpression(node.expression))
return ParseResult.NOT_FOUND;
if (!isIdentifier(node.expression.expression))
return ParseResult.NOT_FOUND;
if (node.expression.expression.text !== "definePlugin")
return ParseResult.NOT_FOUND;
if (!isObjectLiteralExpression(node.expression.arguments[0]))
return ParseResult.INVALID;
let pluginDef;
try {
pluginDef = parseObjLiteralExpr(node.expression.arguments[0], ["name"]);
} catch {
return ParseResult.INVALID;
}
return {
pluginName: pluginDef.name,
posStart: node.expression.pos,
posEnd: node.expression.end,
};
}
export class PluginDefCodeLensProvider implements CodeLensProvider {
check(text: string) {
return text.includes("definePlugin") && text.includes("name:");
}
provideCodeLenses(doc: TextDocument): ProviderResult<CodeLens[]> {
const text = doc.getText();
if (!this.check(text))
return [];
const file = createSourceFile(doc.fileName, text, ScriptTarget.Latest);
const children = file.getChildAt(0)
.getChildren();
const lenses: CodeLens[] = [];
for (const node of children) {
const patchesArray = parsePossiblePatches(node);
if (patchesArray === ParseResult.INVALID)
return [];
if (patchesArray === ParseResult.NOT_FOUND)
continue;
const range = new Range(doc.positionAt(patchesArray.posStart), doc.positionAt(patchesArray.posEnd));
lenses.push(new CodeLens(range, {
title: "Disable Plugin",
command: "equicord-companion.disablePlugin",
arguments: [
{
pluginName: patchesArray.pluginName,
enabled: false,
} satisfies DisablePluginData,
],
}));
lenses.push(new CodeLens(range, {
title: "Enable Plugin",
command: "equicord-companion.disablePlugin",
arguments: [
{
pluginName: patchesArray.pluginName,
enabled: true,
} satisfies DisablePluginData,
],
}));
}
return lenses;
}
}