From 707d688887999b778a93e20fd4e156509056edfb Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 14 May 2025 16:24:18 -0300 Subject: [PATCH 1/3] WebpackPatcher: Try catch more code prone to errors --- src/webpack/patchWebpack.ts | 28 +++++++++++++++++++------- src/webpack/webpack.ts | 40 ++++++++++++++++++++++++++----------- 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 4f5899bc..7d30ee1f 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -60,7 +60,7 @@ export function getFactoryPatchedBy(moduleId: PropertyKey, webpackRequire = wreq return webpackRequire.m[moduleId]?.[SYM_PATCHED_BY]; } -const logger = new Logger("WebpackInterceptor", "#8caaee"); +const logger = new Logger("WebpackPatcher", "#8caaee"); /** Whether we tried to fallback to the WebpackRequire of the factory, or disabled patches */ let wreqFallbackApplied = false; @@ -436,12 +436,21 @@ function runFactoryWithWrap(patchedFactory: PatchedModuleFactory, thisArg: unkno callback(exports, module.id); continue; } + } catch (err) { + logger.error( + "Error while filtering or firing callback for Webpack waitFor subscription:\n", err, + "\n\nModule exports:", exports, + "\n\nFilter:", filter, + "\n\nCallback:", callback + ); + } - if (typeof exports !== "object") { - continue; - } + if (typeof exports !== "object") { + continue; + } - for (const exportKey in exports) { + for (const exportKey in exports) { + try { // Some exports might have not been initialized yet due to circular imports, so try catch it. try { var exportValue = exports[exportKey]; @@ -454,9 +463,14 @@ function runFactoryWithWrap(patchedFactory: PatchedModuleFactory, thisArg: unkno callback(exportValue, module.id); break; } + } catch (err) { + logger.error( + "Error while filtering or firing callback for Webpack waitFor subscription:\n", err, + "\n\nExport value:", exports, + "\n\nFilter:", filter, + "\n\nCallback:", callback + ); } - } catch (err) { - logger.error("Error while firing callback for Webpack waitFor subscription:\n", err, filter, callback); } } diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index c1847474..bce50f3d 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -145,9 +145,17 @@ function makePropertyNonEnumerable(target: Record, key: Proper } export function _blacklistBadModules(requireCache: NonNullable, exports: ModuleExports, moduleId: PropertyKey) { - if (shouldIgnoreValue(exports)) { - makePropertyNonEnumerable(requireCache, moduleId); - return true; + try { + if (shouldIgnoreValue(exports)) { + makePropertyNonEnumerable(requireCache, moduleId); + return true; + } + } catch (err) { + logger.error( + "Error while blacklisting module:\n", err, + "\n\nModule id:", moduleId, + "\n\nModule exports:", exports, + ); } if (typeof exports !== "object") { @@ -156,17 +164,25 @@ export function _blacklistBadModules(requireCache: NonNullable Date: Wed, 14 May 2025 17:08:56 -0300 Subject: [PATCH 2/3] Fix initializing custom themes with ThemeStore too early --- scripts/generateReport.ts | 2 +- src/components/ErrorBoundary.tsx | 8 ++++--- src/debug/runReporter.ts | 4 ++-- .../memberCount/OnlineMemberCountStore.ts | 4 ++-- src/utils/discord.tsx | 4 ++-- src/utils/quickCss.ts | 22 +++++++++++++------ src/webpack/common/internal.tsx | 4 +--- src/webpack/common/menu.ts | 4 ++-- src/webpack/common/react.ts | 3 +-- src/webpack/common/stores.ts | 11 ++++++---- src/webpack/common/utils.ts | 8 +++---- 11 files changed, 42 insertions(+), 32 deletions(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 9502d382..4aad058c 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -261,7 +261,7 @@ page.on("console", async e => { const [, tag, message, otherMessage] = args as Array; switch (tag) { - case "WebpackInterceptor:": + case "WebpackPatcher:": const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/); const patchSlowMatch = message.match(/Patch by (.+?) (took [\d.]+?ms) \(Module id is (.+?)\): (.+)/); const match = patchFailMatch ?? patchSlowMatch; diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index e609d564..0ca20440 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -16,10 +16,10 @@ * along with this program. If not, see . */ +import { LazyComponent, LazyComponentWrapper } from "@utils/lazyReact"; import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; -import { LazyComponent, LazyComponentWrapper } from "@utils/react"; -import { React } from "@webpack/common"; +import type { React } from "@webpack/common"; import { ErrorCard } from "./ErrorCard"; @@ -46,7 +46,9 @@ const NO_ERROR = {}; // We might want to import this in a place where React isn't ready yet. // Thus, wrap in a LazyComponent const ErrorBoundary = LazyComponent(() => { - return class ErrorBoundary extends React.PureComponent> { + // This component is used in a lot of files which end up importing other Webpack commons and causing circular imports. + // For this reason, use a non import access here. + return class ErrorBoundary extends Vencord.Webpack.Common.React.PureComponent> { state = { error: NO_ERROR as any, stack: "", diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index 21802b6a..4ee2d394 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -46,13 +46,13 @@ async function runReporter() { for (const patch of patches) { if (!patch.all) { - new Logger("WebpackInterceptor").warn(`Patch by ${patch.plugin} found no module (Module id is -): ${patch.find}`); + new Logger("WebpackPatcher").warn(`Patch by ${patch.plugin} found no module (Module id is -): ${patch.find}`); } } for (const [plugin, moduleId, match, totalTime] of patchTimings) { if (totalTime > 5) { - new Logger("WebpackInterceptor").warn(`Patch by ${plugin} took ${Math.round(totalTime * 100) / 100}ms (Module id is ${String(moduleId)}): ${match}`); + new Logger("WebpackPatcher").warn(`Patch by ${plugin} took ${Math.round(totalTime * 100) / 100}ms (Module id is ${String(moduleId)}): ${match}`); } } diff --git a/src/plugins/memberCount/OnlineMemberCountStore.ts b/src/plugins/memberCount/OnlineMemberCountStore.ts index d74bea2a..54cc4697 100644 --- a/src/plugins/memberCount/OnlineMemberCountStore.ts +++ b/src/plugins/memberCount/OnlineMemberCountStore.ts @@ -7,7 +7,7 @@ import { proxyLazy } from "@utils/lazy"; import { sleep } from "@utils/misc"; import { Queue } from "@utils/Queue"; -import { Flux, FluxDispatcher, GuildChannelStore, PrivateChannelsStore } from "@webpack/common"; +import { ChannelActionCreators, Flux, FluxDispatcher, GuildChannelStore } from "@webpack/common"; export const OnlineMemberCountStore = proxyLazy(() => { const preloadQueue = new Queue(); @@ -22,7 +22,7 @@ export const OnlineMemberCountStore = proxyLazy(() => { async _ensureCount(guildId: string) { if (onlineMemberMap.has(guildId)) return; - await PrivateChannelsStore.preload(guildId, GuildChannelStore.getDefaultChannel(guildId).id); + await ChannelActionCreators.preload(guildId, GuildChannelStore.getDefaultChannel(guildId).id); } ensureCount(guildId?: string) { diff --git a/src/utils/discord.tsx b/src/utils/discord.tsx index ba60d98a..e04ad201 100644 --- a/src/utils/discord.tsx +++ b/src/utils/discord.tsx @@ -17,7 +17,7 @@ */ import { MessageObject } from "@api/MessageEvents"; -import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; +import { ChannelActionCreators, ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; import { Channel, Guild, Message, User } from "discord-types/general"; import { Except } from "type-fest"; @@ -91,7 +91,7 @@ export function getCurrentGuild(): Guild | undefined { } export function openPrivateChannel(userId: string) { - PrivateChannelsStore.openPrivateChannel(userId); + ChannelActionCreators.openPrivateChannel(userId); } export const enum Theme { diff --git a/src/utils/quickCss.ts b/src/utils/quickCss.ts index c1e11759..1789c14d 100644 --- a/src/utils/quickCss.ts +++ b/src/utils/quickCss.ts @@ -19,7 +19,6 @@ import { Settings, SettingsStore } from "@api/Settings"; import { ThemeStore } from "@webpack/common"; - let style: HTMLStyleElement; let themesStyle: HTMLStyleElement; @@ -61,7 +60,10 @@ async function initThemes() { const { themeLinks, enabledThemes } = Settings; // "darker" and "midnight" both count as dark - const activeTheme = ThemeStore.theme === "light" ? "light" : "dark"; + // This function is first called on DOMContentLoaded, so ThemeStore may not have been loaded yet + const activeTheme = ThemeStore == null + ? undefined + : ThemeStore.theme === "light" ? "light" : "dark"; const links = themeLinks .map(rawLink => { @@ -90,7 +92,6 @@ async function initThemes() { document.addEventListener("DOMContentLoaded", () => { initSystemValues(); - initThemes(); toggle(Settings.useQuickCss); SettingsStore.addChangeListener("useQuickCss", toggle); @@ -98,6 +99,16 @@ document.addEventListener("DOMContentLoaded", () => { SettingsStore.addChangeListener("themeLinks", initThemes); SettingsStore.addChangeListener("enabledThemes", initThemes); + if (!IS_WEB) { + VencordNative.quickCss.addThemeChangeListener(initThemes); + } + + initThemes(); +}); + +export function initQuickCssThemeStore() { + initThemes(); + let currentTheme = ThemeStore.theme; ThemeStore.addChangeListener(() => { if (currentTheme === ThemeStore.theme) return; @@ -105,7 +116,4 @@ document.addEventListener("DOMContentLoaded", () => { currentTheme = ThemeStore.theme; initThemes(); }); - - if (!IS_WEB) - VencordNative.quickCss.addThemeChangeListener(initThemes); -}); +} diff --git a/src/webpack/common/internal.tsx b/src/webpack/common/internal.tsx index 090d9898..5e8c6052 100644 --- a/src/webpack/common/internal.tsx +++ b/src/webpack/common/internal.tsx @@ -17,9 +17,7 @@ */ import { LazyComponent, LazyComponentWrapper } from "@utils/react"; - -// eslint-disable-next-line path-alias/no-relative -import { FilterFn, filters, lazyWebpackSearchHistory, waitFor } from "../webpack"; +import { FilterFn, filters, lazyWebpackSearchHistory, waitFor } from "@webpack"; export function waitForComponent = React.ComponentType & Record>(name: string, filter: FilterFn | string | string[]) { if (IS_REPORTER) lazyWebpackSearchHistory.push(["waitForComponent", Array.isArray(filter) ? filter : [filter]]); diff --git a/src/webpack/common/menu.ts b/src/webpack/common/menu.ts index 5b1056dd..2bb05f36 100644 --- a/src/webpack/common/menu.ts +++ b/src/webpack/common/menu.ts @@ -16,8 +16,8 @@ * along with this program. If not, see . */ -// eslint-disable-next-line path-alias/no-relative -import { filters, mapMangledModuleLazy, waitFor, wreq } from "../webpack"; +import { filters, mapMangledModuleLazy, waitFor, wreq } from "@webpack"; + import type * as t from "./types/menu"; export const Menu = {} as t.Menu; diff --git a/src/webpack/common/react.ts b/src/webpack/common/react.ts index 89b19506..b8b0e753 100644 --- a/src/webpack/common/react.ts +++ b/src/webpack/common/react.ts @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -// eslint-disable-next-line path-alias/no-relative -import { findByCodeLazy, findByPropsLazy, waitFor } from "../webpack"; +import { findByCodeLazy, findByPropsLazy, waitFor } from "@webpack"; export let React: typeof import("react"); export let useState: typeof React.useState; diff --git a/src/webpack/common/stores.ts b/src/webpack/common/stores.ts index 518f13e2..1dcda187 100644 --- a/src/webpack/common/stores.ts +++ b/src/webpack/common/stores.ts @@ -16,10 +16,9 @@ * along with this program. If not, see . */ +import { findByCodeLazy, findByPropsLazy } from "@webpack"; import type * as Stores from "discord-types/stores"; -// eslint-disable-next-line path-alias/no-relative -import { findByCodeLazy, findByPropsLazy } from "../webpack"; import { waitForStore } from "./internal"; import * as t from "./types/stores"; @@ -33,7 +32,7 @@ export let MessageStore: Omit & GenericStore getMessages(chanId: string): any; }; -// this is not actually a FluxStore +// TODO: The correct name for this is ChannelActionCreators and it has already been exported again from utils. Remove this export once enough time has passed export const PrivateChannelsStore = findByPropsLazy("openPrivateChannel"); export let PermissionStore: GenericStore; export let GuildChannelStore: GenericStore; @@ -86,4 +85,8 @@ waitForStore("GuildChannelStore", m => GuildChannelStore = m); waitForStore("MessageStore", m => MessageStore = m); waitForStore("WindowStore", m => WindowStore = m); waitForStore("EmojiStore", m => EmojiStore = m); -waitForStore("ThemeStore", m => ThemeStore = m); +waitForStore("ThemeStore", m => { + ThemeStore = m; + // Importing this directly can easily cause circular imports. For this reason, use a non import access here. + Vencord.QuickCss.initQuickCssThemeStore(); +}); diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index 0396f0f3..05eb5729 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -16,16 +16,15 @@ * along with this program. If not, see . */ +import { _resolveReady, filters, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "@webpack"; import type { Channel } from "discord-types/general"; -// eslint-disable-next-line path-alias/no-relative -import { _resolveReady, filters, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "../webpack"; import type * as t from "./types/utils"; export let FluxDispatcher: t.FluxDispatcher; waitFor(["dispatch", "subscribe"], m => { FluxDispatcher = m; - // Non import call to avoid circular dependency + // Non import access to avoid circular dependency Vencord.Plugins.subscribeAllPluginsFluxEvents(m); const cb = () => { @@ -35,7 +34,7 @@ waitFor(["dispatch", "subscribe"], m => { m.subscribe("CONNECTION_OPEN", cb); }); -export let ComponentDispatch; +export let ComponentDispatch: any; waitFor(["dispatchToLastSubscribed"], m => ComponentDispatch = m); export const Constants: t.Constants = mapMangledModuleLazy('ME:"/users/@me"', { @@ -177,6 +176,7 @@ export const MessageActions = findByPropsLazy("editMessage", "sendMessage"); export const MessageCache = findByPropsLazy("clearCache", "_channelMessages"); export const UserProfileActions = findByPropsLazy("openUserProfileModal", "closeUserProfileModal"); export const InviteActions = findByPropsLazy("resolveInvite"); +export const ChannelActionCreators = findByPropsLazy("openPrivateChannel"); export const IconUtils: t.IconUtils = findByPropsLazy("getGuildBannerURL", "getUserAvatarURL"); From cb8e8bd407db748872dc675b75eca276c23e3365 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 14 May 2025 17:24:34 -0300 Subject: [PATCH 3/3] AccountPanelServerProfile: Fix plugin not working --- src/plugins/accountPanelServerProfile/index.tsx | 2 +- src/plugins/shikiCodeblocks.desktop/api/languages.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/accountPanelServerProfile/index.tsx b/src/plugins/accountPanelServerProfile/index.tsx index ad63ba27..f871b8ee 100644 --- a/src/plugins/accountPanelServerProfile/index.tsx +++ b/src/plugins/accountPanelServerProfile/index.tsx @@ -72,7 +72,7 @@ export default definePlugin({ group: true, replacement: [ { - match: /let{speaking:\i/, + match: /let{ref:\i,speaking:\i/, replace: "$self.useAccountPanelRef();$&" }, { diff --git a/src/plugins/shikiCodeblocks.desktop/api/languages.ts b/src/plugins/shikiCodeblocks.desktop/api/languages.ts index f14a4dc2..baf7a548 100644 --- a/src/plugins/shikiCodeblocks.desktop/api/languages.ts +++ b/src/plugins/shikiCodeblocks.desktop/api/languages.ts @@ -19,7 +19,7 @@ import { ILanguageRegistration } from "@vap/shiki"; export const VPC_REPO = "Vap0r1ze/vapcord"; -export const VPC_REPO_COMMIT = "88a7032a59cca40da170926651b08201ea3b965a"; +export const VPC_REPO_COMMIT = "4d0e4b420fb1e4358852bbd18c804a6f5e54c0d7"; export const vpcRepoAssets = `https://raw.githubusercontent.com/${VPC_REPO}/${VPC_REPO_COMMIT}/assets/shiki-codeblocks`; export const vpcRepoGrammar = (fileName: string) => `${vpcRepoAssets}/${fileName}`; export const vpcRepoLanguages = `${vpcRepoAssets}/languages.json`; @@ -46,7 +46,7 @@ export interface LanguageJson { export const languages: Record = {}; export const loadLanguages = async () => { - const langsJson: LanguageJson[] = await fetch(vpcRepoLanguages).then(res => res.json()); + const langsJson: LanguageJson[] = await fetch(vpcRepoLanguages).then(res => res.ok ? res.json() : []); const loadedLanguages = Object.fromEntries( langsJson.map(lang => [lang.id, { ...lang,