diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c264821e..b7a95cf0 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -37,6 +37,9 @@ jobs:
- name: Build
run: pnpm build --standalone
+ - name: Generate plugin list
+ run: pnpm generatePluginJson dist/plugins.json
+
- name: Clean up obsolete files
run: |
rm -rf dist/extension* Vencord.user.css
diff --git a/.github/workflows/reportBrokenPlugins.yml b/.github/workflows/reportBrokenPlugins.yml
index 719eca75..2e927bb8 100644
--- a/.github/workflows/reportBrokenPlugins.yml
+++ b/.github/workflows/reportBrokenPlugins.yml
@@ -36,7 +36,7 @@ jobs:
export PATH="$PWD/node_modules/.bin:$PATH"
export CHROMIUM_BIN=$(which chromium-browser)
- esbuild test/generateReport.ts > dist/report.mjs
+ esbuild scripts/generateReport.ts > dist/report.mjs
node dist/report.mjs >> $GITHUB_STEP_SUMMARY
env:
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
diff --git a/package.json b/package.json
index 763189ee..c6975a41 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"scripts": {
"build": "node scripts/build/build.mjs",
"buildWeb": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/buildWeb.mjs",
+ "generatePluginJson": "tsx scripts/generatePluginList.ts",
"inject": "node scripts/runInstaller.mjs",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"lint-styles": "stylelint \"src/**/*.css\"",
@@ -59,6 +60,7 @@
"standalone-electron-types": "^1.0.0",
"stylelint": "^14.16.1",
"stylelint-config-standard": "^29.0.0",
+ "tsx": "^3.12.6",
"type-fest": "^3.5.3",
"typescript": "^4.9.4"
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ac7aca77..30ce7bba 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -35,6 +35,7 @@ specifiers:
standalone-electron-types: ^1.0.0
stylelint: ^14.16.1
stylelint-config-standard: ^29.0.0
+ tsx: ^3.12.6
type-fest: ^3.5.3
typescript: ^4.9.4
@@ -67,6 +68,7 @@ devDependencies:
standalone-electron-types: 1.0.0
stylelint: 14.16.1
stylelint-config-standard: 29.0.0_stylelint@14.16.1
+ tsx: 3.12.6
type-fest: 3.5.3
typescript: 4.9.4
@@ -104,6 +106,27 @@ packages:
postcss-selector-parser: 6.0.11
dev: true
+ /@esbuild-kit/cjs-loader/2.4.2:
+ resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==}
+ dependencies:
+ '@esbuild-kit/core-utils': 3.1.0
+ get-tsconfig: 4.4.0
+ dev: true
+
+ /@esbuild-kit/core-utils/3.1.0:
+ resolution: {integrity: sha512-Uuk8RpCg/7fdHSceR1M6XbSZFSuMrxcePFuGgyvsBn+u339dk5OeL4jv2EojwTN2st/unJGsVm4qHWjWNmJ/tw==}
+ dependencies:
+ esbuild: 0.17.12
+ source-map-support: 0.5.21
+ dev: true
+
+ /@esbuild-kit/esm-loader/2.5.5:
+ resolution: {integrity: sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==}
+ dependencies:
+ '@esbuild-kit/core-utils': 3.1.0
+ get-tsconfig: 4.4.0
+ dev: true
+
/@esbuild/android-arm/0.15.18:
resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==}
engines: {node: '>=12'}
@@ -113,6 +136,96 @@ packages:
dev: true
optional: true
+ /@esbuild/android-arm/0.17.12:
+ resolution: {integrity: sha512-E/sgkvwoIfj4aMAPL2e35VnUJspzVYl7+M1B2cqeubdBhADV4uPon0KCc8p2G+LqSJ6i8ocYPCqY3A4GGq0zkQ==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/android-arm64/0.17.12:
+ resolution: {integrity: sha512-WQ9p5oiXXYJ33F2EkE3r0FRDFVpEdcDiwNX3u7Xaibxfx6vQE0Sb8ytrfQsA5WO6kDn6mDfKLh6KrPBjvkk7xA==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/android-x64/0.17.12:
+ resolution: {integrity: sha512-m4OsaCr5gT+se25rFPHKQXARMyAehHTQAz4XX1Vk3d27VtqiX0ALMBPoXZsGaB6JYryCLfgGwUslMqTfqeLU0w==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/darwin-arm64/0.17.12:
+ resolution: {integrity: sha512-O3GCZghRIx+RAN0NDPhyyhRgwa19MoKlzGonIb5hgTj78krqp9XZbYCvFr9N1eUxg0ZQEpiiZ4QvsOQwBpP+lg==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/darwin-x64/0.17.12:
+ resolution: {integrity: sha512-5D48jM3tW27h1qjaD9UNRuN+4v0zvksqZSPZqeSWggfMlsVdAhH3pwSfQIFJwcs9QJ9BRibPS4ViZgs3d2wsCA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/freebsd-arm64/0.17.12:
+ resolution: {integrity: sha512-OWvHzmLNTdF1erSvrfoEBGlN94IE6vCEaGEkEH29uo/VoONqPnoDFfShi41Ew+yKimx4vrmmAJEGNoyyP+OgOQ==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [freebsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/freebsd-x64/0.17.12:
+ resolution: {integrity: sha512-A0Xg5CZv8MU9xh4a+7NUpi5VHBKh1RaGJKqjxe4KG87X+mTjDE6ZvlJqpWoeJxgfXHT7IMP9tDFu7IZ03OtJAw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [freebsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-arm/0.17.12:
+ resolution: {integrity: sha512-WsHyJ7b7vzHdJ1fv67Yf++2dz3D726oO3QCu8iNYik4fb5YuuReOI9OtA+n7Mk0xyQivNTPbl181s+5oZ38gyA==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-arm64/0.17.12:
+ resolution: {integrity: sha512-cK3AjkEc+8v8YG02hYLQIQlOznW+v9N+OI9BAFuyqkfQFR+DnDLhEM5N8QRxAUz99cJTo1rLNXqRrvY15gbQUg==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-ia32/0.17.12:
+ resolution: {integrity: sha512-jdOBXJqcgHlah/nYHnj3Hrnl9l63RjtQ4vn9+bohjQPI2QafASB5MtHAoEv0JQHVb/xYQTFOeuHnNYE1zF7tYw==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@esbuild/linux-loong64/0.15.18:
resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==}
engines: {node: '>=12'}
@@ -122,6 +235,114 @@ packages:
dev: true
optional: true
+ /@esbuild/linux-loong64/0.17.12:
+ resolution: {integrity: sha512-GTOEtj8h9qPKXCyiBBnHconSCV9LwFyx/gv3Phw0pa25qPYjVuuGZ4Dk14bGCfGX3qKF0+ceeQvwmtI+aYBbVA==}
+ engines: {node: '>=12'}
+ cpu: [loong64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-mips64el/0.17.12:
+ resolution: {integrity: sha512-o8CIhfBwKcxmEENOH9RwmUejs5jFiNoDw7YgS0EJTF6kgPgcqLFjgoc5kDey5cMHRVCIWc6kK2ShUePOcc7RbA==}
+ engines: {node: '>=12'}
+ cpu: [mips64el]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-ppc64/0.17.12:
+ resolution: {integrity: sha512-biMLH6NR/GR4z+ap0oJYb877LdBpGac8KfZoEnDiBKd7MD/xt8eaw1SFfYRUeMVx519kVkAOL2GExdFmYnZx3A==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-riscv64/0.17.12:
+ resolution: {integrity: sha512-jkphYUiO38wZGeWlfIBMB72auOllNA2sLfiZPGDtOBb1ELN8lmqBrlMiucgL8awBw1zBXN69PmZM6g4yTX84TA==}
+ engines: {node: '>=12'}
+ cpu: [riscv64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-s390x/0.17.12:
+ resolution: {integrity: sha512-j3ucLdeY9HBcvODhCY4b+Ds3hWGO8t+SAidtmWu/ukfLLG/oYDMaA+dnugTVAg5fnUOGNbIYL9TOjhWgQB8W5g==}
+ engines: {node: '>=12'}
+ cpu: [s390x]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-x64/0.17.12:
+ resolution: {integrity: sha512-uo5JL3cgaEGotaqSaJdRfFNSCUJOIliKLnDGWaVCgIKkHxwhYMm95pfMbWZ9l7GeW9kDg0tSxcy9NYdEtjwwmA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/netbsd-x64/0.17.12:
+ resolution: {integrity: sha512-DNdoRg8JX+gGsbqt2gPgkgb00mqOgOO27KnrWZtdABl6yWTST30aibGJ6geBq3WM2TIeW6COs5AScnC7GwtGPg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [netbsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/openbsd-x64/0.17.12:
+ resolution: {integrity: sha512-aVsENlr7B64w8I1lhHShND5o8cW6sB9n9MUtLumFlPhG3elhNWtE7M1TFpj3m7lT3sKQUMkGFjTQBrvDDO1YWA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [openbsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/sunos-x64/0.17.12:
+ resolution: {integrity: sha512-qbHGVQdKSwi0JQJuZznS4SyY27tYXYF0mrgthbxXrZI3AHKuRvU+Eqbg/F0rmLDpW/jkIZBlCO1XfHUBMNJ1pg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [sunos]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/win32-arm64/0.17.12:
+ resolution: {integrity: sha512-zsCp8Ql+96xXTVTmm6ffvoTSZSV2B/LzzkUXAY33F/76EajNw1m+jZ9zPfNJlJ3Rh4EzOszNDHsmG/fZOhtqDg==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/win32-ia32/0.17.12:
+ resolution: {integrity: sha512-FfrFjR4id7wcFYOdqbDfDET3tjxCozUgbqdkOABsSFzoZGFC92UK7mg4JKRc/B3NNEf1s2WHxJ7VfTdVDPN3ng==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/win32-x64/0.17.12:
+ resolution: {integrity: sha512-JOOxw49BVZx2/5tW3FqkdjSD/5gXYeVGPDcB0lvap0gLQshkh1Nyel1QazC+wNxus3xPlsYAgqU1BUmrmCvWtw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@eslint/eslintrc/1.3.3:
resolution: {integrity: sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -563,6 +784,10 @@ packages:
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
dev: true
+ /buffer-from/1.1.2:
+ resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+ dev: true
+
/buffer/5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
dependencies:
@@ -1047,6 +1272,36 @@ packages:
esbuild-windows-arm64: 0.15.18
dev: true
+ /esbuild/0.17.12:
+ resolution: {integrity: sha512-bX/zHl7Gn2CpQwcMtRogTTBf9l1nl+H6R8nUbjk+RuKqAE3+8FDulLA+pHvX7aA7Xe07Iwa+CWvy9I8Y2qqPKQ==}
+ engines: {node: '>=12'}
+ hasBin: true
+ requiresBuild: true
+ optionalDependencies:
+ '@esbuild/android-arm': 0.17.12
+ '@esbuild/android-arm64': 0.17.12
+ '@esbuild/android-x64': 0.17.12
+ '@esbuild/darwin-arm64': 0.17.12
+ '@esbuild/darwin-x64': 0.17.12
+ '@esbuild/freebsd-arm64': 0.17.12
+ '@esbuild/freebsd-x64': 0.17.12
+ '@esbuild/linux-arm': 0.17.12
+ '@esbuild/linux-arm64': 0.17.12
+ '@esbuild/linux-ia32': 0.17.12
+ '@esbuild/linux-loong64': 0.17.12
+ '@esbuild/linux-mips64el': 0.17.12
+ '@esbuild/linux-ppc64': 0.17.12
+ '@esbuild/linux-riscv64': 0.17.12
+ '@esbuild/linux-s390x': 0.17.12
+ '@esbuild/linux-x64': 0.17.12
+ '@esbuild/netbsd-x64': 0.17.12
+ '@esbuild/openbsd-x64': 0.17.12
+ '@esbuild/sunos-x64': 0.17.12
+ '@esbuild/win32-arm64': 0.17.12
+ '@esbuild/win32-ia32': 0.17.12
+ '@esbuild/win32-x64': 0.17.12
+ dev: true
+
/escape-string-regexp/1.0.5:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
@@ -1400,6 +1655,14 @@ packages:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
dev: true
+ /fsevents/2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/function-bind/1.1.1:
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
dev: true
@@ -1411,6 +1674,10 @@ packages:
pump: 3.0.0
dev: true
+ /get-tsconfig/4.4.0:
+ resolution: {integrity: sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ==}
+ dev: true
+
/get-value/2.0.6:
resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==}
engines: {node: '>=0.10.0'}
@@ -2467,6 +2734,13 @@ packages:
urix: 0.1.0
dev: true
+ /source-map-support/0.5.21:
+ resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+ dependencies:
+ buffer-from: 1.1.2
+ source-map: 0.6.1
+ dev: true
+
/source-map-url/0.4.1:
resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==}
deprecated: See https://github.com/lydell/source-map-url#deprecated
@@ -2477,6 +2751,11 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
+ /source-map/0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
/spdx-correct/3.1.1:
resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
dependencies:
@@ -2739,6 +3018,17 @@ packages:
typescript: 4.9.4
dev: true
+ /tsx/3.12.6:
+ resolution: {integrity: sha512-q93WgS3lBdHlPgS0h1i+87Pt6n9K/qULIMNYZo07nSeu2z5QE2CellcAZfofVXBo2tQg9av2ZcRMQ2S2i5oadQ==}
+ hasBin: true
+ dependencies:
+ '@esbuild-kit/cjs-loader': 2.4.2
+ '@esbuild-kit/core-utils': 3.1.0
+ '@esbuild-kit/esm-loader': 2.5.5
+ optionalDependencies:
+ fsevents: 2.3.2
+ dev: true
+
/type-check/0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
diff --git a/scripts/generatePluginList.ts b/scripts/generatePluginList.ts
new file mode 100644
index 00000000..1f66c3dc
--- /dev/null
+++ b/scripts/generatePluginList.ts
@@ -0,0 +1,191 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Dirent, readdirSync, readFileSync, writeFileSync } from "fs";
+import { access, readFile } from "fs/promises";
+import { join } from "path";
+import { BigIntLiteral, createSourceFile, Identifier, isArrayLiteralExpression, isCallExpression, isExportAssignment, isIdentifier, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isStringLiteral, isVariableStatement, NamedDeclaration, NodeArray, ObjectLiteralExpression, ScriptTarget, StringLiteral, SyntaxKind } from "typescript";
+
+interface Dev {
+ name: string;
+ id: string;
+}
+
+interface PluginData {
+ name: string;
+ description: string;
+ authors: Dev[];
+ dependencies: string[];
+ hasPatches: boolean;
+ hasCommands: boolean;
+ required: boolean;
+ enabledByDefault: boolean;
+ target: "desktop" | "web" | "dev";
+}
+
+const devs = {} as Record;
+
+function getName(node: NamedDeclaration) {
+ return node.name && isIdentifier(node.name) ? node.name.text : undefined;
+}
+
+function hasName(node: NamedDeclaration, name: string) {
+ return getName(node) === name;
+}
+
+function getObjectProp(node: ObjectLiteralExpression, name: string) {
+ const prop = node.properties.find(p => hasName(p, name));
+ if (prop && isPropertyAssignment(prop)) return prop.initializer;
+ return prop;
+}
+
+function parseDevs() {
+ const file = createSourceFile("constants.ts", readFileSync("src/utils/constants.ts", "utf8"), ScriptTarget.Latest);
+
+ for (const child of file.getChildAt(0).getChildren()) {
+ if (!isVariableStatement(child)) continue;
+
+ const devsDeclaration = child.declarationList.declarations.find(d => hasName(d, "Devs"));
+ if (!devsDeclaration?.initializer || !isCallExpression(devsDeclaration.initializer)) continue;
+
+ const value = devsDeclaration.initializer.arguments[0];
+
+ if (!isObjectLiteralExpression(value)) return;
+
+ for (const prop of value.properties) {
+ const name = (prop.name as Identifier).text;
+ const value = isPropertyAssignment(prop) ? prop.initializer : prop;
+
+ if (!isObjectLiteralExpression(value)) throw new Error(`Failed to parse devs: ${name} is not an object literal`);
+
+ devs[name] = {
+ name: (getObjectProp(value, "name") as StringLiteral).text,
+ id: (getObjectProp(value, "id") as BigIntLiteral).text.slice(0, -1)
+ };
+ }
+
+ return;
+ }
+
+ throw new Error("Could not find Devs constant");
+}
+
+async function parseFile(fileName: string) {
+ const file = createSourceFile(fileName, await readFile(fileName, "utf8"), ScriptTarget.Latest);
+
+ const fail = (reason: string) => {
+ return new Error(`Invalid plugin ${fileName}, because ${reason}`);
+ };
+
+ for (const node of file.getChildAt(0).getChildren()) {
+ if (!isExportAssignment(node) || !isCallExpression(node.expression)) continue;
+
+ const call = node.expression;
+ if (!isIdentifier(call.expression) || call.expression.text !== "definePlugin") continue;
+
+ const pluginObj = node.expression.arguments[0];
+ if (!isObjectLiteralExpression(pluginObj)) throw fail("no object literal passed to definePlugin");
+
+ const data = {
+ hasPatches: false,
+ hasCommands: false,
+ enabledByDefault: false,
+ required: false,
+ } as PluginData;
+
+ for (const prop of pluginObj.properties) {
+ const key = getName(prop);
+ const value = isPropertyAssignment(prop) ? prop.initializer : prop;
+
+ switch (key) {
+ case "name":
+ case "description":
+ if (!isStringLiteral(value)) throw fail(`${key} is not a string literal`);
+ data[key] = value.text;
+ break;
+ case "patches":
+ data.hasPatches = true;
+ break;
+ case "commands":
+ data.hasCommands = true;
+ break;
+ case "authors":
+ if (!isArrayLiteralExpression(value)) throw fail("authors is not an array literal");
+ data.authors = value.elements.map(e => {
+ if (!isPropertyAccessExpression(e)) throw fail("authors array contains non-property access expressions");
+ return devs[getName(e)!];
+ });
+ break;
+ case "dependencies":
+ if (!isArrayLiteralExpression(value)) throw fail("dependencies is not an array literal");
+ const { elements } = value;
+ if (elements.some(e => !isStringLiteral(e))) throw fail("dependencies array contains non-string elements");
+ data.dependencies = (elements as NodeArray).map(e => e.text);
+ break;
+ case "required":
+ case "enabledByDefault":
+ data[key] = value.kind === SyntaxKind.TrueKeyword;
+ if (!data[key] && value.kind !== SyntaxKind.FalseKeyword) throw fail(`${key} is not a boolean literal`);
+ break;
+ }
+ }
+
+ if (!data.name || !data.description || !data.authors) throw fail("name, description or authors are missing");
+
+ const fileBits = fileName.split(".");
+ if (fileBits.length > 2 && ["ts", "tsx"].includes(fileBits.at(-1)!)) {
+ const mod = fileBits.at(-2)!;
+ if (!["web", "desktop", "dev"].includes(mod)) throw fail(`invalid target ${fileBits.at(-2)}`);
+ data.target = mod as any;
+ }
+
+ return data;
+ }
+
+ throw fail("no default export called 'definePlugin' found");
+}
+
+async function getEntryPoint(dirent: Dirent) {
+ const base = join("./src/plugins", dirent.name);
+ if (!dirent.isDirectory()) return base;
+
+ for (const name of ["index.ts", "index.tsx"]) {
+ const full = join(base, name);
+ try {
+ await access(full);
+ return full;
+ } catch { }
+ }
+
+ throw new Error(`${dirent.name}: Couldn't find entry point`);
+}
+
+(async () => {
+ parseDevs();
+ const plugins = readdirSync("./src/plugins", { withFileTypes: true }).filter(d => d.name !== "index.ts");
+
+ const promises = plugins.map(async dirent => parseFile(await getEntryPoint(dirent)));
+
+ const data = JSON.stringify(await Promise.all(promises));
+
+ if (process.argv.length > 2) {
+ writeFileSync(process.argv[2], data);
+ } else {
+ console.log(data);
+ }
+})();
diff --git a/test/generateReport.ts b/scripts/generateReport.ts
similarity index 100%
rename from test/generateReport.ts
rename to scripts/generateReport.ts
diff --git a/src/plugins/richerCider.desktop.tsx b/src/plugins/richerCider.desktop.tsx
deleted file mode 100644
index 8b6fb5eb..00000000
--- a/src/plugins/richerCider.desktop.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Vencord, a modification for Discord's desktop app
- * Copyright (c) 2022 OpenAsar
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
-*/
-
-import { Link } from "@components/Link";
-import definePlugin from "@utils/types";
-import { Forms } from "@webpack/common";
-const appIds = [
- "911790844204437504",
- "886578863147192350",
- "1020414178047041627",
- "1032800329332445255"
-];
-export default definePlugin({
- name: "richerCider",
- description: "Enhances Cider (More details in info button) by adding the \"Listening to\" type prefix to the user's rich presence when an applicable ID is found.",
- authors: [{
- id: 191621342473224192n,
- name: "cryptofyre",
- }],
- patches: [
- {
- find: '.displayName="LocalActivityStore"',
- replacement: {
- match: /LOCAL_ACTIVITY_UPDATE:function\((\i)\)\{/,
- replace: "$&$self.patchActivity($1.activity);",
- }
- }
- ],
- settingsAboutComponent: () => (
- <>
- Install Cider to use this Plugin
-
- Follow the link to our website to get Cider up and running, and then enable the plugin.
-
-
- What is Cider?
-
- Cider is an open-source and community oriented Apple Music client for Windows, macOS, and Linux.
-
-
- Recommended Optional Plugins
-
- I'd recommend using TimeBarAllActivities alongside this plugin to give off a much better visual to the eye (Keep in mind this only affects your client and will not show for other users)
-
- >
- ),
- patchActivity(activity: any) {
- if (appIds.includes(activity.application_id)) {
- activity.type = 2; /* LISTENING type */
- }
- },
-});
diff --git a/src/plugins/viewIcons.tsx b/src/plugins/viewIcons.tsx
index 26f29029..7d56538c 100644
--- a/src/plugins/viewIcons.tsx
+++ b/src/plugins/viewIcons.tsx
@@ -19,7 +19,7 @@
import { Devs } from "@utils/constants";
import { LazyComponent } from "@utils/misc";
import { ModalRoot, ModalSize, openModal } from "@utils/modal";
-import { PluginDef } from "@utils/types";
+import definePlugin from "@utils/types";
import { find, findByCode, findByPropsLazy } from "@webpack";
import { Menu } from "@webpack/common";
import type { Guild } from "discord-types/general";
@@ -30,12 +30,12 @@ const MaskedLink = LazyComponent(() => find(m => m.type?.toString().includes("MA
const GuildBannerStore = findByPropsLazy("getGuildBannerURL");
const OPEN_URL = "Vencord.Plugins.plugins.ViewIcons.openImage(";
-export default new class ViewIcons implements PluginDef {
- name = "ViewIcons";
- authors = [Devs.Ven];
- description = "Makes Avatars/Banners in user profiles clickable, and adds Guild Context Menu Entries to View Banner/Icon.";
+export default definePlugin({
+ name: "ViewIcons",
+ authors: [Devs.Ven],
+ description: "Makes Avatars/Banners in user profiles clickable, and adds Guild Context Menu Entries to View Banner/Icon.",
- dependencies = ["MenuItemDeobfuscatorAPI"];
+ dependencies: ["MenuItemDeobfuscatorAPI"],
openImage(url: string) {
const u = new URL(url);
@@ -52,9 +52,9 @@ export default new class ViewIcons implements PluginDef {
/>
));
- }
+ },
- patches = [
+ patches: [
{
find: "onAddFriend:",
replacement: {
@@ -83,7 +83,7 @@ export default new class ViewIcons implements PluginDef {
}
]
}
- ];
+ ],
buildGuildContextMenuEntries(guild: Guild) {
return (
@@ -107,4 +107,4 @@ export default new class ViewIcons implements PluginDef {
);
}
-};
+});