refactor: improve build scripts & automatic testing
- Fix reporter breaking because of ConsoleShortcuts - Fix extractAndLoadChunks issue with 2 match groups; Improve testing of lazy extractAndLoadChunks - Reporter: Properly implement reporter build of Vencord; Test more plugins; Fix running in wrong pages - Fix wrong external files and clean up build script; Remove non used stuff
This commit is contained in:
parent
537fc5e33d
commit
05a40445c8
22 changed files with 225 additions and 176 deletions
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -32,7 +32,7 @@ jobs:
|
||||||
run: pnpm install --frozen-lockfile
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build web
|
- name: Build web
|
||||||
run: pnpm buildWeb --standalone
|
run: pnpm buildWebStandalone
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build --standalone
|
run: pnpm build --standalone
|
||||||
|
|
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
|
@ -32,7 +32,7 @@ jobs:
|
||||||
run: pnpm install --frozen-lockfile
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build web
|
- name: Build web
|
||||||
run: pnpm buildWeb --standalone
|
run: pnpm buildWebStandalone
|
||||||
|
|
||||||
- name: Publish extension
|
- name: Publish extension
|
||||||
run: |
|
run: |
|
||||||
|
|
4
.github/workflows/reportBrokenPlugins.yml
vendored
4
.github/workflows/reportBrokenPlugins.yml
vendored
|
@ -37,8 +37,8 @@ jobs:
|
||||||
with:
|
with:
|
||||||
chrome-version: stable
|
chrome-version: stable
|
||||||
|
|
||||||
- name: Build web
|
- name: Build Vencord Reporter Version
|
||||||
run: pnpm buildWeb --standalone --dev
|
run: pnpm buildReporter
|
||||||
|
|
||||||
- name: Create Report
|
- name: Create Report
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
|
|
@ -20,7 +20,10 @@
|
||||||
"build": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/build.mjs",
|
"build": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/build.mjs",
|
||||||
"buildStandalone": "pnpm build --standalone",
|
"buildStandalone": "pnpm build --standalone",
|
||||||
"buildWeb": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/buildWeb.mjs",
|
"buildWeb": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/buildWeb.mjs",
|
||||||
|
"buildWebStandalone": "pnpm buildWeb --standalone",
|
||||||
|
"buildReporter": "pnpm buildWebStandalone --reporter --skip-extension",
|
||||||
"watch": "pnpm build --watch",
|
"watch": "pnpm build --watch",
|
||||||
|
"watchWeb": "pnpm buildWeb --watch",
|
||||||
"generatePluginJson": "tsx scripts/generatePluginList.ts",
|
"generatePluginJson": "tsx scripts/generatePluginList.ts",
|
||||||
"generateTypes": "tspc --emitDeclarationOnly --declaration --outDir packages/vencord-types",
|
"generateTypes": "tspc --emitDeclarationOnly --declaration --outDir packages/vencord-types",
|
||||||
"inject": "node scripts/runInstaller.mjs",
|
"inject": "node scripts/runInstaller.mjs",
|
||||||
|
|
|
@ -21,19 +21,21 @@ import esbuild from "esbuild";
|
||||||
import { readdir } from "fs/promises";
|
import { readdir } from "fs/promises";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
|
||||||
import { BUILD_TIMESTAMP, commonOpts, existsAsync, globPlugins, isDev, isStandalone, updaterDisabled, VERSION, watch } from "./common.mjs";
|
import { BUILD_TIMESTAMP, commonOpts, exists, globPlugins, IS_DEV, IS_REPORTER, IS_STANDALONE, IS_UPDATER_DISABLED, VERSION, watch } from "./common.mjs";
|
||||||
|
|
||||||
const defines = {
|
const defines = {
|
||||||
IS_STANDALONE: isStandalone,
|
IS_STANDALONE,
|
||||||
IS_DEV: JSON.stringify(isDev),
|
IS_DEV,
|
||||||
IS_UPDATER_DISABLED: updaterDisabled,
|
IS_REPORTER,
|
||||||
|
IS_UPDATER_DISABLED,
|
||||||
IS_WEB: false,
|
IS_WEB: false,
|
||||||
IS_EXTENSION: false,
|
IS_EXTENSION: false,
|
||||||
VERSION: JSON.stringify(VERSION),
|
VERSION: JSON.stringify(VERSION),
|
||||||
BUILD_TIMESTAMP,
|
BUILD_TIMESTAMP
|
||||||
};
|
};
|
||||||
if (defines.IS_STANDALONE === "false")
|
|
||||||
// If this is a local build (not standalone), optimise
|
if (defines.IS_STANDALONE === false)
|
||||||
|
// If this is a local build (not standalone), optimize
|
||||||
// for the specific platform we're on
|
// for the specific platform we're on
|
||||||
defines["process.platform"] = JSON.stringify(process.platform);
|
defines["process.platform"] = JSON.stringify(process.platform);
|
||||||
|
|
||||||
|
@ -46,7 +48,7 @@ const nodeCommonOpts = {
|
||||||
platform: "node",
|
platform: "node",
|
||||||
target: ["esnext"],
|
target: ["esnext"],
|
||||||
external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external],
|
external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external],
|
||||||
define: defines,
|
define: defines
|
||||||
};
|
};
|
||||||
|
|
||||||
const sourceMapFooter = s => watch ? "" : `//# sourceMappingURL=vencord://${s}.js.map`;
|
const sourceMapFooter = s => watch ? "" : `//# sourceMappingURL=vencord://${s}.js.map`;
|
||||||
|
@ -73,13 +75,13 @@ const globNativesPlugin = {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (const dir of pluginDirs) {
|
for (const dir of pluginDirs) {
|
||||||
const dirPath = join("src", dir);
|
const dirPath = join("src", dir);
|
||||||
if (!await existsAsync(dirPath)) continue;
|
if (!await exists(dirPath)) continue;
|
||||||
const plugins = await readdir(dirPath);
|
const plugins = await readdir(dirPath);
|
||||||
for (const p of plugins) {
|
for (const p of plugins) {
|
||||||
const nativePath = join(dirPath, p, "native.ts");
|
const nativePath = join(dirPath, p, "native.ts");
|
||||||
const indexNativePath = join(dirPath, p, "native/index.ts");
|
const indexNativePath = join(dirPath, p, "native/index.ts");
|
||||||
|
|
||||||
if (!(await existsAsync(nativePath)) && !(await existsAsync(indexNativePath)))
|
if (!(await exists(nativePath)) && !(await exists(indexNativePath)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const nameParts = p.split(".");
|
const nameParts = p.split(".");
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { appendFile, mkdir, readdir, readFile, rm, writeFile } from "fs/promises
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import Zip from "zip-local";
|
import Zip from "zip-local";
|
||||||
|
|
||||||
import { BUILD_TIMESTAMP, commonOpts, globPlugins, isDev, VERSION } from "./common.mjs";
|
import { BUILD_TIMESTAMP, commonOpts, globPlugins, IS_DEV, IS_REPORTER, VERSION } from "./common.mjs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {esbuild.BuildOptions}
|
* @type {esbuild.BuildOptions}
|
||||||
|
@ -33,22 +33,23 @@ const commonOptions = {
|
||||||
entryPoints: ["browser/Vencord.ts"],
|
entryPoints: ["browser/Vencord.ts"],
|
||||||
globalName: "Vencord",
|
globalName: "Vencord",
|
||||||
format: "iife",
|
format: "iife",
|
||||||
external: ["plugins", "git-hash", "/assets/*"],
|
external: ["~plugins", "~git-hash", "/assets/*"],
|
||||||
plugins: [
|
plugins: [
|
||||||
globPlugins("web"),
|
globPlugins("web"),
|
||||||
...commonOpts.plugins,
|
...commonOpts.plugins,
|
||||||
],
|
],
|
||||||
target: ["esnext"],
|
target: ["esnext"],
|
||||||
define: {
|
define: {
|
||||||
IS_WEB: "true",
|
IS_WEB: true,
|
||||||
IS_EXTENSION: "false",
|
IS_EXTENSION: false,
|
||||||
IS_STANDALONE: "true",
|
IS_STANDALONE: true,
|
||||||
IS_DEV: JSON.stringify(isDev),
|
IS_DEV,
|
||||||
IS_DISCORD_DESKTOP: "false",
|
IS_REPORTER,
|
||||||
IS_VESKTOP: "false",
|
IS_DISCORD_DESKTOP: false,
|
||||||
IS_UPDATER_DISABLED: "true",
|
IS_VESKTOP: false,
|
||||||
|
IS_UPDATER_DISABLED: true,
|
||||||
VERSION: JSON.stringify(VERSION),
|
VERSION: JSON.stringify(VERSION),
|
||||||
BUILD_TIMESTAMP,
|
BUILD_TIMESTAMP
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -87,16 +88,16 @@ await Promise.all(
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
...commonOptions,
|
...commonOptions,
|
||||||
outfile: "dist/browser.js",
|
outfile: "dist/browser.js",
|
||||||
footer: { js: "//# sourceURL=VencordWeb" },
|
footer: { js: "//# sourceURL=VencordWeb" }
|
||||||
}),
|
}),
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
...commonOptions,
|
...commonOptions,
|
||||||
outfile: "dist/extension.js",
|
outfile: "dist/extension.js",
|
||||||
define: {
|
define: {
|
||||||
...commonOptions?.define,
|
...commonOptions?.define,
|
||||||
IS_EXTENSION: "true",
|
IS_EXTENSION: true,
|
||||||
},
|
},
|
||||||
footer: { js: "//# sourceURL=VencordWeb" },
|
footer: { js: "//# sourceURL=VencordWeb" }
|
||||||
}),
|
}),
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
...commonOptions,
|
...commonOptions,
|
||||||
|
@ -112,7 +113,7 @@ await Promise.all(
|
||||||
footer: {
|
footer: {
|
||||||
// UserScripts get wrapped in an iife, so define Vencord prop on window that returns our local
|
// UserScripts get wrapped in an iife, so define Vencord prop on window that returns our local
|
||||||
js: "Object.defineProperty(unsafeWindow,'Vencord',{get:()=>Vencord});"
|
js: "Object.defineProperty(unsafeWindow,'Vencord',{get:()=>Vencord});"
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -165,7 +166,7 @@ async function buildExtension(target, files) {
|
||||||
f.startsWith("manifest") ? "manifest.json" : f,
|
f.startsWith("manifest") ? "manifest.json" : f,
|
||||||
content
|
content
|
||||||
];
|
];
|
||||||
}))),
|
})))
|
||||||
};
|
};
|
||||||
|
|
||||||
await rm(target, { recursive: true, force: true });
|
await rm(target, { recursive: true, force: true });
|
||||||
|
@ -192,6 +193,7 @@ const appendCssRuntime = readFile("dist/Vencord.user.css", "utf-8").then(content
|
||||||
return appendFile("dist/Vencord.user.js", cssRuntime);
|
return appendFile("dist/Vencord.user.js", cssRuntime);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!process.argv.includes("--skip-extension")) {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
appendCssRuntime,
|
appendCssRuntime,
|
||||||
buildExtension("chromium-unpacked", ["modifyResponseHeaders.json", "content.js", "manifest.json", "icon.png"]),
|
buildExtension("chromium-unpacked", ["modifyResponseHeaders.json", "content.js", "manifest.json", "icon.png"]),
|
||||||
|
@ -203,3 +205,7 @@ console.info("Packed Chromium Extension written to dist/extension-chrome.zip");
|
||||||
|
|
||||||
Zip.sync.zip("dist/firefox-unpacked").compress().save("dist/extension-firefox.zip");
|
Zip.sync.zip("dist/firefox-unpacked").compress().save("dist/extension-firefox.zip");
|
||||||
console.info("Packed Firefox Extension written to dist/extension-firefox.zip");
|
console.info("Packed Firefox Extension written to dist/extension-firefox.zip");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
await appendCssRuntime;
|
||||||
|
}
|
||||||
|
|
|
@ -35,24 +35,26 @@ const PackageJSON = JSON.parse(readFileSync("package.json"));
|
||||||
export const VERSION = PackageJSON.version;
|
export const VERSION = PackageJSON.version;
|
||||||
// https://reproducible-builds.org/docs/source-date-epoch/
|
// https://reproducible-builds.org/docs/source-date-epoch/
|
||||||
export const BUILD_TIMESTAMP = Number(process.env.SOURCE_DATE_EPOCH) || Date.now();
|
export const BUILD_TIMESTAMP = Number(process.env.SOURCE_DATE_EPOCH) || Date.now();
|
||||||
|
|
||||||
export const watch = process.argv.includes("--watch");
|
export const watch = process.argv.includes("--watch");
|
||||||
export const isDev = watch || process.argv.includes("--dev");
|
export const IS_DEV = watch || process.argv.includes("--dev");
|
||||||
export const isStandalone = JSON.stringify(process.argv.includes("--standalone"));
|
export const IS_REPORTER = process.argv.includes("--reporter");
|
||||||
export const updaterDisabled = JSON.stringify(process.argv.includes("--disable-updater"));
|
export const IS_STANDALONE = process.argv.includes("--standalone");
|
||||||
|
|
||||||
|
export const IS_UPDATER_DISABLED = process.argv.includes("--disable-updater");
|
||||||
export const gitHash = process.env.VENCORD_HASH || execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
|
export const gitHash = process.env.VENCORD_HASH || execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
|
||||||
|
|
||||||
export const banner = {
|
export const banner = {
|
||||||
js: `
|
js: `
|
||||||
// Vencord ${gitHash}
|
// Vencord ${gitHash}
|
||||||
// Standalone: ${isStandalone}
|
// Standalone: ${IS_STANDALONE}
|
||||||
// Platform: ${isStandalone === "false" ? process.platform : "Universal"}
|
// Platform: ${IS_STANDALONE === false ? process.platform : "Universal"}
|
||||||
// Updater disabled: ${updaterDisabled}
|
// Updater Disabled: ${IS_UPDATER_DISABLED}
|
||||||
`.trim()
|
`.trim()
|
||||||
};
|
};
|
||||||
|
|
||||||
const isWeb = process.argv.slice(0, 2).some(f => f.endsWith("buildWeb.mjs"));
|
export async function exists(path) {
|
||||||
|
return await access(path, FsConstants.F_OK)
|
||||||
export function existsAsync(path) {
|
|
||||||
return access(path, FsConstants.F_OK)
|
|
||||||
.then(() => true)
|
.then(() => true)
|
||||||
.catch(() => false);
|
.catch(() => false);
|
||||||
}
|
}
|
||||||
|
@ -66,7 +68,7 @@ export const makeAllPackagesExternalPlugin = {
|
||||||
setup(build) {
|
setup(build) {
|
||||||
const filter = /^[^./]|^\.[^./]|^\.\.[^/]/; // Must not start with "/" or "./" or "../"
|
const filter = /^[^./]|^\.[^./]|^\.\.[^/]/; // Must not start with "/" or "./" or "../"
|
||||||
build.onResolve({ filter }, args => ({ path: args.path, external: true }));
|
build.onResolve({ filter }, args => ({ path: args.path, external: true }));
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,14 +91,14 @@ export const globPlugins = kind => ({
|
||||||
let plugins = "\n";
|
let plugins = "\n";
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (const dir of pluginDirs) {
|
for (const dir of pluginDirs) {
|
||||||
if (!await existsAsync(`./src/${dir}`)) continue;
|
if (!await exists(`./src/${dir}`)) continue;
|
||||||
const files = await readdir(`./src/${dir}`);
|
const files = await readdir(`./src/${dir}`);
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (file.startsWith("_") || file.startsWith(".")) continue;
|
if (file.startsWith("_") || file.startsWith(".")) continue;
|
||||||
if (file === "index.ts") continue;
|
if (file === "index.ts") continue;
|
||||||
|
|
||||||
const target = getPluginTarget(file);
|
const target = getPluginTarget(file);
|
||||||
if (target) {
|
if (target && !IS_REPORTER) {
|
||||||
if (target === "dev" && !watch) continue;
|
if (target === "dev" && !watch) continue;
|
||||||
if (target === "web" && kind === "discordDesktop") continue;
|
if (target === "web" && kind === "discordDesktop") continue;
|
||||||
if (target === "desktop" && kind === "web") continue;
|
if (target === "desktop" && kind === "web") continue;
|
||||||
|
@ -178,7 +180,7 @@ export const fileUrlPlugin = {
|
||||||
build.onLoad({ filter, namespace: "file-uri" }, async ({ pluginData: { path, uri } }) => {
|
build.onLoad({ filter, namespace: "file-uri" }, async ({ pluginData: { path, uri } }) => {
|
||||||
const { searchParams } = new URL(uri);
|
const { searchParams } = new URL(uri);
|
||||||
const base64 = searchParams.has("base64");
|
const base64 = searchParams.has("base64");
|
||||||
const minify = isStandalone === "true" && searchParams.has("minify");
|
const minify = IS_STANDALONE === true && searchParams.has("minify");
|
||||||
const noTrim = searchParams.get("trim") === "false";
|
const noTrim = searchParams.get("trim") === "false";
|
||||||
|
|
||||||
const encoding = base64 ? "base64" : "utf-8";
|
const encoding = base64 ? "base64" : "utf-8";
|
||||||
|
|
|
@ -205,7 +205,12 @@ page.on("console", async e => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isVencord) {
|
if (isVencord) {
|
||||||
const args = await Promise.all(e.args().map(a => a.jsonValue()));
|
let args: unknown[] = [];
|
||||||
|
try {
|
||||||
|
args = await Promise.all(e.args().map(a => a.jsonValue()));
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const [, tag, message] = args as Array<string>;
|
const [, tag, message] = args as Array<string>;
|
||||||
const cause = await maybeGetError(e.args()[3]);
|
const cause = await maybeGetError(e.args()[3]);
|
||||||
|
@ -277,7 +282,7 @@ page.on("pageerror", e => console.error("[Page Error]", e));
|
||||||
|
|
||||||
await page.setBypassCSP(true);
|
await page.setBypassCSP(true);
|
||||||
|
|
||||||
async function runtime(token: string) {
|
async function reporterRuntime(token: string) {
|
||||||
console.log("[PUP_DEBUG]", "Starting test...");
|
console.log("[PUP_DEBUG]", "Starting test...");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -285,43 +290,7 @@ async function runtime(token: string) {
|
||||||
Object.defineProperty(navigator, "languages", {
|
Object.defineProperty(navigator, "languages", {
|
||||||
get: function () {
|
get: function () {
|
||||||
return ["en-US", "en"];
|
return ["en-US", "en"];
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Monkey patch Logger to not log with custom css
|
|
||||||
// @ts-ignore
|
|
||||||
const originalLog = Vencord.Util.Logger.prototype._log;
|
|
||||||
// @ts-ignore
|
|
||||||
Vencord.Util.Logger.prototype._log = function (level, levelColor, args) {
|
|
||||||
if (level === "warn" || level === "error")
|
|
||||||
return console[level]("[Vencord]", this.name + ":", ...args);
|
|
||||||
|
|
||||||
return originalLog.call(this, level, levelColor, args);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Force enable all plugins and patches
|
|
||||||
Vencord.Plugins.patches.length = 0;
|
|
||||||
Object.values(Vencord.Plugins.plugins).forEach(p => {
|
|
||||||
// Needs native server to run
|
|
||||||
if (p.name === "WebRichPresence (arRPC)") return;
|
|
||||||
|
|
||||||
Vencord.Settings.plugins[p.name].enabled = true;
|
|
||||||
p.patches?.forEach(patch => {
|
|
||||||
patch.plugin = p.name;
|
|
||||||
delete patch.predicate;
|
|
||||||
delete patch.group;
|
|
||||||
|
|
||||||
Vencord.Util.canonicalizeFind(patch);
|
|
||||||
if (!Array.isArray(patch.replacement)) {
|
|
||||||
patch.replacement = [patch.replacement];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
patch.replacement.forEach(r => {
|
|
||||||
delete r.predicate;
|
|
||||||
});
|
|
||||||
|
|
||||||
Vencord.Plugins.patches.push(patch);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let wreq: typeof Vencord.Webpack.wreq;
|
let wreq: typeof Vencord.Webpack.wreq;
|
||||||
|
@ -338,7 +307,7 @@ async function runtime(token: string) {
|
||||||
// True if resolved, false otherwise
|
// True if resolved, false otherwise
|
||||||
const chunksSearchPromises = [] as Array<() => boolean>;
|
const chunksSearchPromises = [] as Array<() => boolean>;
|
||||||
|
|
||||||
const LazyChunkRegex = canonicalizeMatch(/(?:Promise\.all\(\[(\i\.\i\("[^)]+?"\)[^\]]+?)\]\)|(\i\.\i\("[^)]+?"\)))\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/g);
|
const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("[^)]+?"\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/g);
|
||||||
|
|
||||||
async function searchAndLoadLazyChunks(factoryCode: string) {
|
async function searchAndLoadLazyChunks(factoryCode: string) {
|
||||||
const lazyChunks = factoryCode.matchAll(LazyChunkRegex);
|
const lazyChunks = factoryCode.matchAll(LazyChunkRegex);
|
||||||
|
@ -348,8 +317,7 @@ async function runtime(token: string) {
|
||||||
// the chunk containing the component
|
// the chunk containing the component
|
||||||
const shouldForceDefer = factoryCode.includes(".Messages.GUILD_FEED_UNFEATURE_BUTTON_TEXT");
|
const shouldForceDefer = factoryCode.includes(".Messages.GUILD_FEED_UNFEATURE_BUTTON_TEXT");
|
||||||
|
|
||||||
await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIdsArray, rawChunkIdsSingle, entryPoint]) => {
|
await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIds, entryPoint]) => {
|
||||||
const rawChunkIds = rawChunkIdsArray ?? rawChunkIdsSingle;
|
|
||||||
const chunkIds = rawChunkIds ? Array.from(rawChunkIds.matchAll(Vencord.Webpack.ChunkIdsRegex)).map(m => m[1]) : [];
|
const chunkIds = rawChunkIds ? Array.from(rawChunkIds.matchAll(Vencord.Webpack.ChunkIdsRegex)).map(m => m[1]) : [];
|
||||||
|
|
||||||
if (chunkIds.length === 0) {
|
if (chunkIds.length === 0) {
|
||||||
|
@ -520,14 +488,14 @@ async function runtime(token: string) {
|
||||||
} else if (method === "extractAndLoadChunks") {
|
} else if (method === "extractAndLoadChunks") {
|
||||||
const [code, matcher] = args;
|
const [code, matcher] = args;
|
||||||
|
|
||||||
const module = Vencord.Webpack.findModuleFactory(...code);
|
result = await Vencord.Webpack.extractAndLoadChunks(code, matcher);
|
||||||
if (module) result = module.toString().match(canonicalizeMatch(matcher));
|
if (result === false) result = null;
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
result = Vencord.Webpack[method](...args);
|
result = Vencord.Webpack[method](...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == null || ("$$vencordInternal" in result && result.$$vencordInternal() == null)) throw "a rock at ben shapiro";
|
if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw "a rock at ben shapiro";
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
let logMessage = searchType;
|
let logMessage = searchType;
|
||||||
if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") logMessage += `(${args[0].toString().slice(0, 147)}...)`;
|
if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") logMessage += `(${args[0].toString().slice(0, 147)}...)`;
|
||||||
|
@ -545,9 +513,10 @@ async function runtime(token: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.evaluateOnNewDocument(`
|
await page.evaluateOnNewDocument(`
|
||||||
${readFileSync("./dist/browser.js", "utf-8")}
|
if (location.host.endsWith("discord.com")) {
|
||||||
|
${readFileSync("./dist/browser.js", "utf-8")};
|
||||||
;(${runtime.toString()})(${JSON.stringify(process.env.DISCORD_TOKEN)});
|
(${reporterRuntime.toString()})(${JSON.stringify(process.env.DISCORD_TOKEN)});
|
||||||
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
await page.goto(CANARY ? "https://canary.discord.com/login" : "https://discord.com/login");
|
await page.goto(CANARY ? "https://canary.discord.com/login" : "https://discord.com/login");
|
||||||
|
|
|
@ -18,14 +18,14 @@
|
||||||
|
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
|
|
||||||
if (IS_DEV) {
|
if (IS_DEV || IS_REPORTER) {
|
||||||
var traces = {} as Record<string, [number, any[]]>;
|
var traces = {} as Record<string, [number, any[]]>;
|
||||||
var logger = new Logger("Tracer", "#FFD166");
|
var logger = new Logger("Tracer", "#FFD166");
|
||||||
}
|
}
|
||||||
|
|
||||||
const noop = function () { };
|
const noop = function () { };
|
||||||
|
|
||||||
export const beginTrace = !IS_DEV ? noop :
|
export const beginTrace = !(IS_DEV || IS_REPORTER) ? noop :
|
||||||
function beginTrace(name: string, ...args: any[]) {
|
function beginTrace(name: string, ...args: any[]) {
|
||||||
if (name in traces)
|
if (name in traces)
|
||||||
throw new Error(`Trace ${name} already exists!`);
|
throw new Error(`Trace ${name} already exists!`);
|
||||||
|
@ -33,7 +33,7 @@ export const beginTrace = !IS_DEV ? noop :
|
||||||
traces[name] = [performance.now(), args];
|
traces[name] = [performance.now(), args];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const finishTrace = !IS_DEV ? noop : function finishTrace(name: string) {
|
export const finishTrace = !(IS_DEV || IS_REPORTER) ? noop : function finishTrace(name: string) {
|
||||||
const end = performance.now();
|
const end = performance.now();
|
||||||
|
|
||||||
const [start, args] = traces[name];
|
const [start, args] = traces[name];
|
||||||
|
@ -48,7 +48,7 @@ type TraceNameMapper<F extends Func> = (...args: Parameters<F>) => string;
|
||||||
const noopTracer =
|
const noopTracer =
|
||||||
<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>) => f;
|
<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>) => f;
|
||||||
|
|
||||||
export const traceFunction = !IS_DEV
|
export const traceFunction = !(IS_DEV || IS_REPORTER)
|
||||||
? noopTracer
|
? noopTracer
|
||||||
: function traceFunction<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>): F {
|
: function traceFunction<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>): F {
|
||||||
return function (this: any, ...args: Parameters<F>) {
|
return function (this: any, ...args: Parameters<F>) {
|
||||||
|
|
3
src/globals.d.ts
vendored
3
src/globals.d.ts
vendored
|
@ -34,9 +34,10 @@ declare global {
|
||||||
*/
|
*/
|
||||||
export var IS_WEB: boolean;
|
export var IS_WEB: boolean;
|
||||||
export var IS_EXTENSION: boolean;
|
export var IS_EXTENSION: boolean;
|
||||||
export var IS_DEV: boolean;
|
|
||||||
export var IS_STANDALONE: boolean;
|
export var IS_STANDALONE: boolean;
|
||||||
export var IS_UPDATER_DISABLED: boolean;
|
export var IS_UPDATER_DISABLED: boolean;
|
||||||
|
export var IS_DEV: boolean;
|
||||||
|
export var IS_REPORTER: boolean;
|
||||||
export var IS_DISCORD_DESKTOP: boolean;
|
export var IS_DISCORD_DESKTOP: boolean;
|
||||||
export var IS_VESKTOP: boolean;
|
export var IS_VESKTOP: boolean;
|
||||||
export var VERSION: string;
|
export var VERSION: string;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
import { popNotice, showNotice } from "@api/Notices";
|
import { popNotice, showNotice } from "@api/Notices";
|
||||||
import { Link } from "@components/Link";
|
import { Link } from "@components/Link";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin, { ReporterTestable } from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common";
|
import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common";
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ export default definePlugin({
|
||||||
name: "WebRichPresence (arRPC)",
|
name: "WebRichPresence (arRPC)",
|
||||||
description: "Client plugin for arRPC to enable RPC on Discord Web (experimental)",
|
description: "Client plugin for arRPC to enable RPC on Discord Web (experimental)",
|
||||||
authors: [Devs.Ducko],
|
authors: [Devs.Ducko],
|
||||||
|
reporterTestable: ReporterTestable.None,
|
||||||
|
|
||||||
settingsAboutComponent: () => (
|
settingsAboutComponent: () => (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
|
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
|
||||||
import { filters, findAll, search } from "@webpack";
|
import { filters, findAll, search } from "@webpack";
|
||||||
|
|
||||||
const PORT = 8485;
|
const PORT = 8485;
|
||||||
|
@ -243,6 +243,7 @@ export default definePlugin({
|
||||||
name: "DevCompanion",
|
name: "DevCompanion",
|
||||||
description: "Dev Companion Plugin",
|
description: "Dev Companion Plugin",
|
||||||
authors: [Devs.Ven],
|
authors: [Devs.Ven],
|
||||||
|
reporterTestable: ReporterTestable.None,
|
||||||
settings,
|
settings,
|
||||||
|
|
||||||
toolboxActions: {
|
toolboxActions: {
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { addContextMenuPatch, removeContextMenuPatch } from "@api/ContextMenu";
|
||||||
import { Settings } from "@api/Settings";
|
import { Settings } from "@api/Settings";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { canonicalizeFind } from "@utils/patches";
|
import { canonicalizeFind } from "@utils/patches";
|
||||||
import { Patch, Plugin, StartAt } from "@utils/types";
|
import { Patch, Plugin, ReporterTestable, StartAt } from "@utils/types";
|
||||||
import { FluxDispatcher } from "@webpack/common";
|
import { FluxDispatcher } from "@webpack/common";
|
||||||
import { FluxEvents } from "@webpack/types";
|
import { FluxEvents } from "@webpack/types";
|
||||||
|
|
||||||
|
@ -39,35 +39,68 @@ export const patches = [] as Patch[];
|
||||||
let enabledPluginsSubscribedFlux = false;
|
let enabledPluginsSubscribedFlux = false;
|
||||||
const subscribedFluxEventsPlugins = new Set<string>();
|
const subscribedFluxEventsPlugins = new Set<string>();
|
||||||
|
|
||||||
|
const pluginsValues = Object.values(Plugins);
|
||||||
const settings = Settings.plugins;
|
const settings = Settings.plugins;
|
||||||
|
|
||||||
export function isPluginEnabled(p: string) {
|
export function isPluginEnabled(p: string) {
|
||||||
return (
|
return (
|
||||||
|
IS_REPORTER ||
|
||||||
Plugins[p]?.required ||
|
Plugins[p]?.required ||
|
||||||
Plugins[p]?.isDependency ||
|
Plugins[p]?.isDependency ||
|
||||||
settings[p]?.enabled
|
settings[p]?.enabled
|
||||||
) ?? false;
|
) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pluginsValues = Object.values(Plugins);
|
export function addPatch(newPatch: Omit<Patch, "plugin">, pluginName: string) {
|
||||||
|
const patch = newPatch as Patch;
|
||||||
|
patch.plugin = pluginName;
|
||||||
|
|
||||||
// First roundtrip to mark and force enable dependencies (only for enabled plugins)
|
if (IS_REPORTER) {
|
||||||
|
delete patch.predicate;
|
||||||
|
delete patch.group;
|
||||||
|
}
|
||||||
|
|
||||||
|
canonicalizeFind(patch);
|
||||||
|
if (!Array.isArray(patch.replacement)) {
|
||||||
|
patch.replacement = [patch.replacement];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_REPORTER) {
|
||||||
|
patch.replacement.forEach(r => {
|
||||||
|
delete r.predicate;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
patches.push(patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isReporterTestable(p: Plugin, part: ReporterTestable) {
|
||||||
|
return p.reporterTestable == null
|
||||||
|
? true
|
||||||
|
: (p.reporterTestable & part) === part;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First round-trip to mark and force enable dependencies
|
||||||
//
|
//
|
||||||
// FIXME: might need to revisit this if there's ever nested (dependencies of dependencies) dependencies since this only
|
// FIXME: might need to revisit this if there's ever nested (dependencies of dependencies) dependencies since this only
|
||||||
// goes for the top level and their children, but for now this works okay with the current API plugins
|
// goes for the top level and their children, but for now this works okay with the current API plugins
|
||||||
for (const p of pluginsValues) if (settings[p.name]?.enabled) {
|
for (const p of pluginsValues) if (isPluginEnabled(p.name)) {
|
||||||
p.dependencies?.forEach(d => {
|
p.dependencies?.forEach(d => {
|
||||||
const dep = Plugins[d];
|
const dep = Plugins[d];
|
||||||
if (dep) {
|
|
||||||
|
if (!dep) {
|
||||||
|
const error = new Error(`Plugin ${p.name} has unresolved dependency ${d}`);
|
||||||
|
|
||||||
|
if (IS_DEV) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.warn(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
settings[d].enabled = true;
|
settings[d].enabled = true;
|
||||||
dep.isDependency = true;
|
dep.isDependency = true;
|
||||||
}
|
|
||||||
else {
|
|
||||||
const error = new Error(`Plugin ${p.name} has unresolved dependency ${d}`);
|
|
||||||
if (IS_DEV)
|
|
||||||
throw error;
|
|
||||||
logger.warn(error);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,23 +115,18 @@ for (const p of pluginsValues) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p.patches && isPluginEnabled(p.name)) {
|
if (p.patches && isPluginEnabled(p.name)) {
|
||||||
|
if (!IS_REPORTER || isReporterTestable(p, ReporterTestable.Patches)) {
|
||||||
for (const patch of p.patches) {
|
for (const patch of p.patches) {
|
||||||
patch.plugin = p.name;
|
addPatch(patch, p.name);
|
||||||
|
|
||||||
canonicalizeFind(patch);
|
|
||||||
if (!Array.isArray(patch.replacement)) {
|
|
||||||
patch.replacement = [patch.replacement];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
patches.push(patch);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const startAllPlugins = traceFunction("startAllPlugins", function startAllPlugins(target: StartAt) {
|
export const startAllPlugins = traceFunction("startAllPlugins", function startAllPlugins(target: StartAt) {
|
||||||
logger.info(`Starting plugins (stage ${target})`);
|
logger.info(`Starting plugins (stage ${target})`);
|
||||||
for (const name in Plugins)
|
for (const name in Plugins) {
|
||||||
if (isPluginEnabled(name)) {
|
if (isPluginEnabled(name) && (!IS_REPORTER || isReporterTestable(Plugins[name], ReporterTestable.Start))) {
|
||||||
const p = Plugins[name];
|
const p = Plugins[name];
|
||||||
|
|
||||||
const startAt = p.startAt ?? StartAt.WebpackReady;
|
const startAt = p.startAt ?? StartAt.WebpackReady;
|
||||||
|
@ -106,30 +134,38 @@ export const startAllPlugins = traceFunction("startAllPlugins", function startAl
|
||||||
|
|
||||||
startPlugin(Plugins[name]);
|
startPlugin(Plugins[name]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export function startDependenciesRecursive(p: Plugin) {
|
export function startDependenciesRecursive(p: Plugin) {
|
||||||
let restartNeeded = false;
|
let restartNeeded = false;
|
||||||
const failures: string[] = [];
|
const failures: string[] = [];
|
||||||
p.dependencies?.forEach(dep => {
|
|
||||||
if (!Settings.plugins[dep].enabled) {
|
p.dependencies?.forEach(d => {
|
||||||
startDependenciesRecursive(Plugins[dep]);
|
if (!settings[d].enabled) {
|
||||||
|
const dep = Plugins[d];
|
||||||
|
startDependenciesRecursive(dep);
|
||||||
|
|
||||||
// If the plugin has patches, don't start the plugin, just enable it.
|
// If the plugin has patches, don't start the plugin, just enable it.
|
||||||
Settings.plugins[dep].enabled = true;
|
settings[d].enabled = true;
|
||||||
if (Plugins[dep].patches) {
|
dep.isDependency = true;
|
||||||
logger.warn(`Enabling dependency ${dep} requires restart.`);
|
|
||||||
|
if (dep.patches) {
|
||||||
|
logger.warn(`Enabling dependency ${d} requires restart.`);
|
||||||
restartNeeded = true;
|
restartNeeded = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const result = startPlugin(Plugins[dep]);
|
|
||||||
if (!result) failures.push(dep);
|
const result = startPlugin(dep);
|
||||||
|
if (!result) failures.push(d);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { restartNeeded, failures };
|
return { restartNeeded, failures };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function subscribePluginFluxEvents(p: Plugin, fluxDispatcher: typeof FluxDispatcher) {
|
export function subscribePluginFluxEvents(p: Plugin, fluxDispatcher: typeof FluxDispatcher) {
|
||||||
if (p.flux && !subscribedFluxEventsPlugins.has(p.name)) {
|
if (p.flux && !subscribedFluxEventsPlugins.has(p.name) && (!IS_REPORTER || isReporterTestable(p, ReporterTestable.FluxEvents))) {
|
||||||
subscribedFluxEventsPlugins.add(p.name);
|
subscribedFluxEventsPlugins.add(p.name);
|
||||||
|
|
||||||
logger.debug("Subscribing to flux events of plugin", p.name);
|
logger.debug("Subscribing to flux events of plugin", p.name);
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { definePluginSettings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { getStegCloak } from "@utils/dependencies";
|
import { getStegCloak } from "@utils/dependencies";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
|
||||||
import { ChannelStore, Constants, RestAPI, Tooltip } from "@webpack/common";
|
import { ChannelStore, Constants, RestAPI, Tooltip } from "@webpack/common";
|
||||||
import { Message } from "discord-types/general";
|
import { Message } from "discord-types/general";
|
||||||
|
|
||||||
|
@ -105,6 +105,9 @@ export default definePlugin({
|
||||||
description: "Encrypt your Messages in a non-suspicious way!",
|
description: "Encrypt your Messages in a non-suspicious way!",
|
||||||
authors: [Devs.SammCheese],
|
authors: [Devs.SammCheese],
|
||||||
dependencies: ["MessagePopoverAPI", "ChatInputButtonAPI", "MessageUpdaterAPI"],
|
dependencies: ["MessagePopoverAPI", "ChatInputButtonAPI", "MessageUpdaterAPI"],
|
||||||
|
reporterTestable: ReporterTestable.Patches,
|
||||||
|
settings,
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
// Indicator
|
// Indicator
|
||||||
|
@ -121,7 +124,6 @@ export default definePlugin({
|
||||||
URL_REGEX: new RegExp(
|
URL_REGEX: new RegExp(
|
||||||
/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/,
|
/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/,
|
||||||
),
|
),
|
||||||
settings,
|
|
||||||
async start() {
|
async start() {
|
||||||
addButton("InvisibleChat", message => {
|
addButton("InvisibleChat", message => {
|
||||||
return this.INV_REGEX.test(message?.content)
|
return this.INV_REGEX.test(message?.content)
|
||||||
|
|
|
@ -20,7 +20,7 @@ import "./shiki.css";
|
||||||
|
|
||||||
import { enableStyle } from "@api/Styles";
|
import { enableStyle } from "@api/Styles";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin, { ReporterTestable } from "@utils/types";
|
||||||
import previewExampleText from "file://previewExample.tsx";
|
import previewExampleText from "file://previewExample.tsx";
|
||||||
|
|
||||||
import { shiki } from "./api/shiki";
|
import { shiki } from "./api/shiki";
|
||||||
|
@ -34,6 +34,9 @@ export default definePlugin({
|
||||||
name: "ShikiCodeblocks",
|
name: "ShikiCodeblocks",
|
||||||
description: "Brings vscode-style codeblocks into Discord, powered by Shiki",
|
description: "Brings vscode-style codeblocks into Discord, powered by Shiki",
|
||||||
authors: [Devs.Vap],
|
authors: [Devs.Vap],
|
||||||
|
reporterTestable: ReporterTestable.Patches,
|
||||||
|
settings,
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "codeBlock:{react(",
|
find: "codeBlock:{react(",
|
||||||
|
@ -66,7 +69,6 @@ export default definePlugin({
|
||||||
isPreview: true,
|
isPreview: true,
|
||||||
tempSettings,
|
tempSettings,
|
||||||
}),
|
}),
|
||||||
settings,
|
|
||||||
|
|
||||||
// exports
|
// exports
|
||||||
shiki,
|
shiki,
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { Devs } from "@utils/constants";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { wordsToTitle } from "@utils/text";
|
import { wordsToTitle } from "@utils/text";
|
||||||
import definePlugin, { OptionType, PluginOptionsItem } from "@utils/types";
|
import definePlugin, { OptionType, PluginOptionsItem, ReporterTestable } from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { Button, ChannelStore, Forms, GuildMemberStore, SelectedChannelStore, SelectedGuildStore, useMemo, UserStore } from "@webpack/common";
|
import { Button, ChannelStore, Forms, GuildMemberStore, SelectedChannelStore, SelectedGuildStore, useMemo, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
|
@ -155,6 +155,7 @@ export default definePlugin({
|
||||||
name: "VcNarrator",
|
name: "VcNarrator",
|
||||||
description: "Announces when users join, leave, or move voice channels via narrator",
|
description: "Announces when users join, leave, or move voice channels via narrator",
|
||||||
authors: [Devs.Ven],
|
authors: [Devs.Ven],
|
||||||
|
reporterTestable: ReporterTestable.None,
|
||||||
|
|
||||||
flux: {
|
flux: {
|
||||||
VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) {
|
VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { definePluginSettings } from "@api/Settings";
|
||||||
import { makeRange } from "@components/PluginSettings/components";
|
import { makeRange } from "@components/PluginSettings/components";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import definePlugin, { OptionType, PluginNative } from "@utils/types";
|
import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { ChannelStore, GuildStore, UserStore } from "@webpack/common";
|
import { ChannelStore, GuildStore, UserStore } from "@webpack/common";
|
||||||
import type { Channel, Embed, GuildMember, MessageAttachment, User } from "discord-types/general";
|
import type { Channel, Embed, GuildMember, MessageAttachment, User } from "discord-types/general";
|
||||||
|
@ -143,7 +143,9 @@ export default definePlugin({
|
||||||
description: "Forwards discord notifications to XSOverlay, for easy viewing in VR",
|
description: "Forwards discord notifications to XSOverlay, for easy viewing in VR",
|
||||||
authors: [Devs.Nyako],
|
authors: [Devs.Nyako],
|
||||||
tags: ["vr", "notify"],
|
tags: ["vr", "notify"],
|
||||||
|
reporterTestable: ReporterTestable.None,
|
||||||
settings,
|
settings,
|
||||||
|
|
||||||
flux: {
|
flux: {
|
||||||
CALL_UPDATE({ call }: { call: Call; }) {
|
CALL_UPDATE({ call }: { call: Call; }) {
|
||||||
if (call?.ringing?.includes(UserStore.getCurrentUser().id) && settings.store.callNotifications) {
|
if (call?.ringing?.includes(UserStore.getCurrentUser().id) && settings.store.callNotifications) {
|
||||||
|
|
|
@ -32,6 +32,11 @@ export class Logger {
|
||||||
constructor(public name: string, public color: string = "white") { }
|
constructor(public name: string, public color: string = "white") { }
|
||||||
|
|
||||||
private _log(level: "log" | "error" | "warn" | "info" | "debug", levelColor: string, args: any[], customFmt = "") {
|
private _log(level: "log" | "error" | "warn" | "info" | "debug", levelColor: string, args: any[], customFmt = "") {
|
||||||
|
if (IS_REPORTER && (level === "warn" || level === "error")) {
|
||||||
|
console[level]("[Vencord]", this.name + ":", ...args);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
console[level](
|
console[level](
|
||||||
`%c Vencord %c %c ${this.name} ${customFmt}`,
|
`%c Vencord %c %c ${this.name} ${customFmt}`,
|
||||||
`background: ${levelColor}; color: black; font-weight: bold; border-radius: 5px;`,
|
`background: ${levelColor}; color: black; font-weight: bold; border-radius: 5px;`,
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { makeLazy } from "./lazy";
|
import { makeLazy } from "./lazy";
|
||||||
import { EXTENSION_BASE_URL } from "./web-metadata";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Add dynamically loaded dependencies for plugins here.
|
Add dynamically loaded dependencies for plugins here.
|
||||||
|
@ -67,15 +66,6 @@ export interface ApngFrameData {
|
||||||
playTime: number;
|
playTime: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// On web (extensions), use extension uri as basepath (load files from extension)
|
|
||||||
// On desktop (electron), load from cdn
|
|
||||||
export const rnnoiseDist = IS_EXTENSION
|
|
||||||
? new URL("/third-party/rnnoise", EXTENSION_BASE_URL).toString()
|
|
||||||
: "https://unpkg.com/@sapphi-red/web-noise-suppressor@0.3.3/dist";
|
|
||||||
export const rnnoiseWasmSrc = (simd = false) => `${rnnoiseDist}/rnnoise${simd ? "_simd" : ""}.wasm`;
|
|
||||||
export const rnnoiseWorkletSrc = `${rnnoiseDist}/rnnoise/workletProcessor.js`;
|
|
||||||
|
|
||||||
|
|
||||||
// The below code is only used on the Desktop (electron) build of Vencord.
|
// The below code is only used on the Desktop (electron) build of Vencord.
|
||||||
// Browser (extension) builds do not contain these remote imports.
|
// Browser (extension) builds do not contain these remote imports.
|
||||||
|
|
||||||
|
|
|
@ -94,6 +94,10 @@ export interface PluginDef {
|
||||||
* @default StartAt.WebpackReady
|
* @default StartAt.WebpackReady
|
||||||
*/
|
*/
|
||||||
startAt?: StartAt,
|
startAt?: StartAt,
|
||||||
|
/**
|
||||||
|
* Which parts of the plugin can be tested by the reporter. Defaults to all parts
|
||||||
|
*/
|
||||||
|
reporterTestable?: number;
|
||||||
/**
|
/**
|
||||||
* Optionally provide settings that the user can configure in the Plugins tab of settings.
|
* Optionally provide settings that the user can configure in the Plugins tab of settings.
|
||||||
* @deprecated Use `settings` instead
|
* @deprecated Use `settings` instead
|
||||||
|
@ -144,6 +148,13 @@ export const enum StartAt {
|
||||||
WebpackReady = "WebpackReady"
|
WebpackReady = "WebpackReady"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum ReporterTestable {
|
||||||
|
None = 1 << 1,
|
||||||
|
Start = 1 << 2,
|
||||||
|
Patches = 1 << 3,
|
||||||
|
FluxEvents = 1 << 4
|
||||||
|
}
|
||||||
|
|
||||||
export const enum OptionType {
|
export const enum OptionType {
|
||||||
STRING,
|
STRING,
|
||||||
NUMBER,
|
NUMBER,
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { LazyComponent } from "@utils/react";
|
||||||
import { FilterFn, filters, lazyWebpackSearchHistory, waitFor } from "../webpack";
|
import { FilterFn, filters, lazyWebpackSearchHistory, waitFor } from "../webpack";
|
||||||
|
|
||||||
export function waitForComponent<T extends React.ComponentType<any> = React.ComponentType<any> & Record<string, any>>(name: string, filter: FilterFn | string | string[]): T {
|
export function waitForComponent<T extends React.ComponentType<any> = React.ComponentType<any> & Record<string, any>>(name: string, filter: FilterFn | string | string[]): T {
|
||||||
if (IS_DEV) lazyWebpackSearchHistory.push(["waitForComponent", Array.isArray(filter) ? filter : [filter]]);
|
if (IS_REPORTER) lazyWebpackSearchHistory.push(["waitForComponent", Array.isArray(filter) ? filter : [filter]]);
|
||||||
|
|
||||||
let myValue: T = function () {
|
let myValue: T = function () {
|
||||||
throw new Error(`Vencord could not find the ${name} Component`);
|
throw new Error(`Vencord could not find the ${name} Component`);
|
||||||
|
@ -38,7 +38,7 @@ export function waitForComponent<T extends React.ComponentType<any> = React.Comp
|
||||||
}
|
}
|
||||||
|
|
||||||
export function waitForStore(name: string, cb: (v: any) => void) {
|
export function waitForStore(name: string, cb: (v: any) => void) {
|
||||||
if (IS_DEV) lazyWebpackSearchHistory.push(["waitForStore", [name]]);
|
if (IS_REPORTER) lazyWebpackSearchHistory.push(["waitForStore", [name]]);
|
||||||
|
|
||||||
waitFor(filters.byStoreName(name), cb, { isIndirect: true });
|
waitFor(filters.byStoreName(name), cb, { isIndirect: true });
|
||||||
}
|
}
|
||||||
|
|
|
@ -264,7 +264,7 @@ export const lazyWebpackSearchHistory = [] as Array<["find" | "findByProps" | "f
|
||||||
* @example const mod = proxyLazy(() => findByProps("blah")); console.log(mod.blah);
|
* @example const mod = proxyLazy(() => findByProps("blah")); console.log(mod.blah);
|
||||||
*/
|
*/
|
||||||
export function proxyLazyWebpack<T = any>(factory: () => any, attempts?: number) {
|
export function proxyLazyWebpack<T = any>(factory: () => any, attempts?: number) {
|
||||||
if (IS_DEV) lazyWebpackSearchHistory.push(["proxyLazyWebpack", [factory]]);
|
if (IS_REPORTER) lazyWebpackSearchHistory.push(["proxyLazyWebpack", [factory]]);
|
||||||
|
|
||||||
return proxyLazy<T>(factory, attempts);
|
return proxyLazy<T>(factory, attempts);
|
||||||
}
|
}
|
||||||
|
@ -278,7 +278,7 @@ export function proxyLazyWebpack<T = any>(factory: () => any, attempts?: number)
|
||||||
* @returns Result of factory function
|
* @returns Result of factory function
|
||||||
*/
|
*/
|
||||||
export function LazyComponentWebpack<T extends object = any>(factory: () => any, attempts?: number) {
|
export function LazyComponentWebpack<T extends object = any>(factory: () => any, attempts?: number) {
|
||||||
if (IS_DEV) lazyWebpackSearchHistory.push(["LazyComponentWebpack", [factory]]);
|
if (IS_REPORTER) lazyWebpackSearchHistory.push(["LazyComponentWebpack", [factory]]);
|
||||||
|
|
||||||
return LazyComponent<T>(factory, attempts);
|
return LazyComponent<T>(factory, attempts);
|
||||||
}
|
}
|
||||||
|
@ -287,7 +287,7 @@ export function LazyComponentWebpack<T extends object = any>(factory: () => any,
|
||||||
* Find the first module that matches the filter, lazily
|
* Find the first module that matches the filter, lazily
|
||||||
*/
|
*/
|
||||||
export function findLazy(filter: FilterFn) {
|
export function findLazy(filter: FilterFn) {
|
||||||
if (IS_DEV) lazyWebpackSearchHistory.push(["find", [filter]]);
|
if (IS_REPORTER) lazyWebpackSearchHistory.push(["find", [filter]]);
|
||||||
|
|
||||||
return proxyLazy(() => find(filter));
|
return proxyLazy(() => find(filter));
|
||||||
}
|
}
|
||||||
|
@ -306,7 +306,7 @@ export function findByProps(...props: string[]) {
|
||||||
* Find the first module that has the specified properties, lazily
|
* Find the first module that has the specified properties, lazily
|
||||||
*/
|
*/
|
||||||
export function findByPropsLazy(...props: string[]) {
|
export function findByPropsLazy(...props: string[]) {
|
||||||
if (IS_DEV) lazyWebpackSearchHistory.push(["findByProps", props]);
|
if (IS_REPORTER) lazyWebpackSearchHistory.push(["findByProps", props]);
|
||||||
|
|
||||||
return proxyLazy(() => findByProps(...props));
|
return proxyLazy(() => findByProps(...props));
|
||||||
}
|
}
|
||||||
|
@ -325,7 +325,7 @@ export function findByCode(...code: string[]) {
|
||||||
* Find the first function that includes all the given code, lazily
|
* Find the first function that includes all the given code, lazily
|
||||||
*/
|
*/
|
||||||
export function findByCodeLazy(...code: string[]) {
|
export function findByCodeLazy(...code: string[]) {
|
||||||
if (IS_DEV) lazyWebpackSearchHistory.push(["findByCode", code]);
|
if (IS_REPORTER) lazyWebpackSearchHistory.push(["findByCode", code]);
|
||||||
|
|
||||||
return proxyLazy(() => findByCode(...code));
|
return proxyLazy(() => findByCode(...code));
|
||||||
}
|
}
|
||||||
|
@ -344,7 +344,7 @@ export function findStore(name: string) {
|
||||||
* Find a store by its displayName, lazily
|
* Find a store by its displayName, lazily
|
||||||
*/
|
*/
|
||||||
export function findStoreLazy(name: string) {
|
export function findStoreLazy(name: string) {
|
||||||
if (IS_DEV) lazyWebpackSearchHistory.push(["findStore", [name]]);
|
if (IS_REPORTER) lazyWebpackSearchHistory.push(["findStore", [name]]);
|
||||||
|
|
||||||
return proxyLazy(() => findStore(name));
|
return proxyLazy(() => findStore(name));
|
||||||
}
|
}
|
||||||
|
@ -363,7 +363,7 @@ export function findComponentByCode(...code: string[]) {
|
||||||
* Finds the first component that matches the filter, lazily.
|
* Finds the first component that matches the filter, lazily.
|
||||||
*/
|
*/
|
||||||
export function findComponentLazy<T extends object = any>(filter: FilterFn) {
|
export function findComponentLazy<T extends object = any>(filter: FilterFn) {
|
||||||
if (IS_DEV) lazyWebpackSearchHistory.push(["findComponent", [filter]]);
|
if (IS_REPORTER) lazyWebpackSearchHistory.push(["findComponent", [filter]]);
|
||||||
|
|
||||||
|
|
||||||
return LazyComponent<T>(() => {
|
return LazyComponent<T>(() => {
|
||||||
|
@ -378,7 +378,7 @@ export function findComponentLazy<T extends object = any>(filter: FilterFn) {
|
||||||
* Finds the first component that includes all the given code, lazily
|
* Finds the first component that includes all the given code, lazily
|
||||||
*/
|
*/
|
||||||
export function findComponentByCodeLazy<T extends object = any>(...code: string[]) {
|
export function findComponentByCodeLazy<T extends object = any>(...code: string[]) {
|
||||||
if (IS_DEV) lazyWebpackSearchHistory.push(["findComponentByCode", code]);
|
if (IS_REPORTER) lazyWebpackSearchHistory.push(["findComponentByCode", code]);
|
||||||
|
|
||||||
return LazyComponent<T>(() => {
|
return LazyComponent<T>(() => {
|
||||||
const res = find(filters.componentByCode(...code), { isIndirect: true });
|
const res = find(filters.componentByCode(...code), { isIndirect: true });
|
||||||
|
@ -392,7 +392,7 @@ export function findComponentByCodeLazy<T extends object = any>(...code: string[
|
||||||
* Finds the first component that is exported by the first prop name, lazily
|
* Finds the first component that is exported by the first prop name, lazily
|
||||||
*/
|
*/
|
||||||
export function findExportedComponentLazy<T extends object = any>(...props: string[]) {
|
export function findExportedComponentLazy<T extends object = any>(...props: string[]) {
|
||||||
if (IS_DEV) lazyWebpackSearchHistory.push(["findExportedComponent", props]);
|
if (IS_REPORTER) lazyWebpackSearchHistory.push(["findExportedComponent", props]);
|
||||||
|
|
||||||
return LazyComponent<T>(() => {
|
return LazyComponent<T>(() => {
|
||||||
const res = find(filters.byProps(...props), { isIndirect: true });
|
const res = find(filters.byProps(...props), { isIndirect: true });
|
||||||
|
@ -402,14 +402,14 @@ export function findExportedComponentLazy<T extends object = any>(...props: stri
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DefaultExtractAndLoadChunksRegex = /(?:Promise\.all\(\[(\i\.\i\("[^)]+?"\)[^\]]+?)\]\)|(\i\.\i\("[^)]+?"\))|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/;
|
export const DefaultExtractAndLoadChunksRegex = /(?:(?:Promise\.all\(\[)?(\i\.e\("[^)]+?"\)[^\]]*?)(?:\]\))?|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/;
|
||||||
export const ChunkIdsRegex = /\("(.+?)"\)/g;
|
export const ChunkIdsRegex = /\("([^"]+?)"\)/g;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract and load chunks using their entry point
|
* Extract and load chunks using their entry point
|
||||||
* @param code An array of all the code the module factory containing the lazy chunk loading must include
|
* @param code An array of all the code the module factory containing the lazy chunk loading must include
|
||||||
* @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the lazy chunk loading found in the module factory
|
* @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the first lazy chunk loading found in the module factory
|
||||||
* @returns A promise that resolves when the chunks were loaded
|
* @returns A promise that resolves with a boolean whether the chunks were loaded
|
||||||
*/
|
*/
|
||||||
export async function extractAndLoadChunks(code: string[], matcher: RegExp = DefaultExtractAndLoadChunksRegex) {
|
export async function extractAndLoadChunks(code: string[], matcher: RegExp = DefaultExtractAndLoadChunksRegex) {
|
||||||
const module = findModuleFactory(...code);
|
const module = findModuleFactory(...code);
|
||||||
|
@ -417,7 +417,11 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def
|
||||||
const err = new Error("extractAndLoadChunks: Couldn't find module factory");
|
const err = new Error("extractAndLoadChunks: Couldn't find module factory");
|
||||||
logger.warn(err, "Code:", code, "Matcher:", matcher);
|
logger.warn(err, "Code:", code, "Matcher:", matcher);
|
||||||
|
|
||||||
return;
|
// Strict behaviour in DevBuilds to fail early and make sure the issue is found
|
||||||
|
if (IS_DEV && !devToolsOpen)
|
||||||
|
throw err;
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const match = module.toString().match(canonicalizeMatch(matcher));
|
const match = module.toString().match(canonicalizeMatch(matcher));
|
||||||
|
@ -429,10 +433,10 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def
|
||||||
if (IS_DEV && !devToolsOpen)
|
if (IS_DEV && !devToolsOpen)
|
||||||
throw err;
|
throw err;
|
||||||
|
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [, rawChunkIdsArray, rawChunkIdsSingle, entryPointId] = match;
|
const [, rawChunkIds, entryPointId] = match;
|
||||||
if (Number.isNaN(Number(entryPointId))) {
|
if (Number.isNaN(Number(entryPointId))) {
|
||||||
const err = new Error("extractAndLoadChunks: Matcher didn't return a capturing group with the chunk ids array, or the entry point id returned as the second group wasn't a number");
|
const err = new Error("extractAndLoadChunks: Matcher didn't return a capturing group with the chunk ids array, or the entry point id returned as the second group wasn't a number");
|
||||||
logger.warn(err, "Code:", code, "Matcher:", matcher);
|
logger.warn(err, "Code:", code, "Matcher:", matcher);
|
||||||
|
@ -441,16 +445,27 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def
|
||||||
if (IS_DEV && !devToolsOpen)
|
if (IS_DEV && !devToolsOpen)
|
||||||
throw err;
|
throw err;
|
||||||
|
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rawChunkIds = rawChunkIdsArray ?? rawChunkIdsSingle;
|
|
||||||
if (rawChunkIds) {
|
if (rawChunkIds) {
|
||||||
const chunkIds = Array.from(rawChunkIds.matchAll(ChunkIdsRegex)).map((m: any) => m[1]);
|
const chunkIds = Array.from(rawChunkIds.matchAll(ChunkIdsRegex)).map((m: any) => m[1]);
|
||||||
await Promise.all(chunkIds.map(id => wreq.e(id)));
|
await Promise.all(chunkIds.map(id => wreq.e(id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wreq.m[entryPointId] == null) {
|
||||||
|
const err = new Error("extractAndLoadChunks: Entry point is not loaded in the module factories, perhaps one of the chunks failed to load");
|
||||||
|
logger.warn(err, "Code:", code, "Matcher:", matcher);
|
||||||
|
|
||||||
|
// Strict behaviour in DevBuilds to fail early and make sure the issue is found
|
||||||
|
if (IS_DEV && !devToolsOpen)
|
||||||
|
throw err;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
wreq(entryPointId);
|
wreq(entryPointId);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -458,11 +473,11 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def
|
||||||
*
|
*
|
||||||
* Extract and load chunks using their entry point
|
* Extract and load chunks using their entry point
|
||||||
* @param code An array of all the code the module factory containing the lazy chunk loading must include
|
* @param code An array of all the code the module factory containing the lazy chunk loading must include
|
||||||
* @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the lazy chunk loading found in the module factory
|
* @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the first lazy chunk loading found in the module factory
|
||||||
* @returns A function that returns a promise that resolves when the chunks were loaded, on first call
|
* @returns A function that returns a promise that resolves with a boolean whether the chunks were loaded, on first call
|
||||||
*/
|
*/
|
||||||
export function extractAndLoadChunksLazy(code: string[], matcher = DefaultExtractAndLoadChunksRegex) {
|
export function extractAndLoadChunksLazy(code: string[], matcher = DefaultExtractAndLoadChunksRegex) {
|
||||||
if (IS_DEV) lazyWebpackSearchHistory.push(["extractAndLoadChunks", [code, matcher]]);
|
if (IS_REPORTER) lazyWebpackSearchHistory.push(["extractAndLoadChunks", [code, matcher]]);
|
||||||
|
|
||||||
return makeLazy(() => extractAndLoadChunks(code, matcher));
|
return makeLazy(() => extractAndLoadChunks(code, matcher));
|
||||||
}
|
}
|
||||||
|
@ -472,7 +487,7 @@ export function extractAndLoadChunksLazy(code: string[], matcher = DefaultExtrac
|
||||||
* then call the callback with the module as the first argument
|
* then call the callback with the module as the first argument
|
||||||
*/
|
*/
|
||||||
export function waitFor(filter: string | string[] | FilterFn, callback: CallbackFn, { isIndirect = false }: { isIndirect?: boolean; } = {}) {
|
export function waitFor(filter: string | string[] | FilterFn, callback: CallbackFn, { isIndirect = false }: { isIndirect?: boolean; } = {}) {
|
||||||
if (IS_DEV && !isIndirect) lazyWebpackSearchHistory.push(["waitFor", Array.isArray(filter) ? filter : [filter]]);
|
if (IS_REPORTER && !isIndirect) lazyWebpackSearchHistory.push(["waitFor", Array.isArray(filter) ? filter : [filter]]);
|
||||||
|
|
||||||
if (typeof filter === "string")
|
if (typeof filter === "string")
|
||||||
filter = filters.byProps(filter);
|
filter = filters.byProps(filter);
|
||||||
|
|
Loading…
Reference in a new issue