From 306890aa139995947f5cb02f92ca40aa966e7fbf Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 12 Feb 2025 14:03:18 -0300 Subject: [PATCH 01/34] WebpackPatcher: Use way less closures (#3217) --- eslint.config.mjs | 2 +- scripts/generateReport.ts | 4 - src/debug/loadLazyChunks.ts | 6 +- src/debug/runReporter.ts | 6 +- src/plugins/_core/noTrack.ts | 2 +- src/plugins/index.ts | 3 +- src/webpack/patchWebpack.ts | 445 +++++++++++++++++------------------ src/webpack/webpack.ts | 2 +- src/webpack/wreq.d.ts | 6 +- tsconfig.json | 4 +- 10 files changed, 236 insertions(+), 244 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 67327b93..d59c3753 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -134,7 +134,7 @@ export default tseslint.config( "no-unsafe-optional-chaining": "error", "no-useless-backreference": "error", "use-isnan": "error", - "prefer-const": "error", + "prefer-const": ["error", { destructuring: "all" }], "prefer-spread": "error", // Plugin Rules diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 5cab1b46..7bfda763 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -16,11 +16,7 @@ * along with this program. If not, see . */ -/* eslint-disable no-fallthrough */ - -// eslint-disable-next-line spaced-comment /// -// eslint-disable-next-line spaced-comment /// import { createHmac } from "crypto"; diff --git a/src/debug/loadLazyChunks.ts b/src/debug/loadLazyChunks.ts index 21207855..427acd11 100644 --- a/src/debug/loadLazyChunks.ts +++ b/src/debug/loadLazyChunks.ts @@ -8,7 +8,7 @@ import { Logger } from "@utils/Logger"; import { canonicalizeMatch } from "@utils/patches"; import * as Webpack from "@webpack"; import { wreq } from "@webpack"; -import { AnyModuleFactory, ModuleFactory } from "webpack"; +import { AnyModuleFactory, ModuleFactory } from "@webpack/wreq.d"; export async function loadLazyChunks() { const LazyChunkLoaderLogger = new Logger("LazyChunkLoader"); @@ -140,8 +140,8 @@ export async function loadLazyChunks() { } Webpack.factoryListeners.add(factoryListener); - for (const factoryId in wreq.m) { - factoryListener(wreq.m[factoryId]); + for (const moduleId in wreq.m) { + factoryListener(wreq.m[moduleId]); } await chunksSearchingDone; diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index 8d4194bc..7a14609e 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -6,9 +6,9 @@ import { Logger } from "@utils/Logger"; import * as Webpack from "@webpack"; -import { addPatch, patches } from "plugins"; -import { getBuildNumber } from "webpack/patchWebpack"; +import { getBuildNumber, patchTimings } from "@webpack/patcher"; +import { addPatch, patches } from "../plugins"; import { loadLazyChunks } from "./loadLazyChunks"; async function runReporter() { @@ -51,7 +51,7 @@ async function runReporter() { } } - for (const [plugin, moduleId, match, totalTime] of Vencord.WebpackPatcher.patchTimings) { + 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}`); } diff --git a/src/plugins/_core/noTrack.ts b/src/plugins/_core/noTrack.ts index 58d8d42a..30920a06 100644 --- a/src/plugins/_core/noTrack.ts +++ b/src/plugins/_core/noTrack.ts @@ -20,7 +20,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType, StartAt } from "@utils/types"; -import { WebpackRequire } from "webpack"; +import { WebpackRequire } from "@webpack/wreq.d"; const settings = definePluginSettings({ disableAnalytics: { diff --git a/src/plugins/index.ts b/src/plugins/index.ts index e1899b74..4a268868 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -31,6 +31,7 @@ import { Logger } from "@utils/Logger"; import { canonicalizeFind, canonicalizeReplacement } from "@utils/patches"; import { Patch, Plugin, PluginDef, ReporterTestable, StartAt } from "@utils/types"; import { FluxDispatcher } from "@webpack/common"; +import { patches } from "@webpack/patcher"; import { FluxEvents } from "@webpack/types"; import Plugins from "~plugins"; @@ -41,7 +42,7 @@ const logger = new Logger("PluginManager", "#a6d189"); export const PMLogger = logger; export const plugins = Plugins; -export const patches = [] as Patch[]; +export { patches }; /** Whether we have subscribed to flux events of all the enabled plugins when FluxDispatcher was ready */ let enabledPluginsSubscribedFlux = false; diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 87036237..14c1888c 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -9,32 +9,23 @@ import { makeLazy } from "@utils/lazy"; import { Logger } from "@utils/Logger"; import { interpolateIfDefined } from "@utils/misc"; import { canonicalizeReplacement } from "@utils/patches"; -import { PatchReplacement } from "@utils/types"; +import { Patch, PatchReplacement } from "@utils/types"; import { traceFunctionWithResults } from "../debug/Tracer"; -import { patches } from "../plugins"; -import { _initWebpack, _shouldIgnoreModule, AnyModuleFactory, AnyWebpackRequire, factoryListeners, findModuleId, MaybeWrappedModuleFactory, ModuleExports, moduleListeners, waitForSubscriptions, WebpackRequire, WrappedModuleFactory, wreq } from "."; +import { _initWebpack, _shouldIgnoreModule, factoryListeners, findModuleId, moduleListeners, waitForSubscriptions, wreq } from "./webpack"; +import { AnyModuleFactory, AnyWebpackRequire, MaybePatchedModuleFactory, ModuleExports, PatchedModuleFactory, WebpackRequire } from "./wreq.d"; + +export const patches = [] as Patch[]; export const SYM_ORIGINAL_FACTORY = Symbol("WebpackPatcher.originalFactory"); export const SYM_PATCHED_SOURCE = Symbol("WebpackPatcher.patchedSource"); export const SYM_PATCHED_BY = Symbol("WebpackPatcher.patchedBy"); -/** A set with all the Webpack instances */ export const allWebpackInstances = new Set(); -export const patchTimings = [] as Array<[plugin: string, moduleId: PropertyKey, match: string | RegExp, totalTime: number]>; -const logger = new Logger("WebpackInterceptor", "#8caaee"); -/** Whether we tried to fallback to factory WebpackRequire, or disabled patches */ -let wreqFallbackApplied = false; -/** Whether we should be patching factories. - * - * This should be disabled if we start searching for the module to get the build number, and then resumed once it's done. - * */ -let shouldPatchFactories = true; +export const patchTimings = [] as Array<[plugin: string, moduleId: PropertyKey, match: PatchReplacement["match"], totalTime: number]>; export const getBuildNumber = makeLazy(() => { try { - shouldPatchFactories = false; - try { if (wreq.m[128014]?.toString().includes("Trying to open a changelog for an invalid build number")) { const hardcodedGetBuildNumber = wreq(128014).b as () => number; @@ -59,13 +50,23 @@ export const getBuildNumber = makeLazy(() => { return typeof buildNumber === "number" ? buildNumber : -1; } catch { return -1; - } finally { - shouldPatchFactories = true; } }); -type Define = typeof Reflect.defineProperty; -const define: Define = (target, p, attributes) => { +export function getFactoryPatchedSource(moduleId: PropertyKey, webpackRequire = wreq as AnyWebpackRequire) { + return webpackRequire.m[moduleId]?.[SYM_PATCHED_SOURCE]; +} + +export function getFactoryPatchedBy(moduleId: PropertyKey, webpackRequire = wreq as AnyWebpackRequire) { + return webpackRequire.m[moduleId]?.[SYM_PATCHED_BY]; +} + +const logger = new Logger("WebpackInterceptor", "#8caaee"); + +/** Whether we tried to fallback to the WebpackRequire of the factory, or disabled patches */ +let wreqFallbackApplied = false; + +const define: typeof Reflect.defineProperty = (target, p, attributes) => { if (Object.hasOwn(attributes, "value")) { attributes.writable = true; } @@ -77,22 +78,17 @@ const define: Define = (target, p, attributes) => { }); }; -export function getOriginalFactory(id: PropertyKey, webpackRequire = wreq as AnyWebpackRequire) { - const moduleFactory = webpackRequire.m[id]; - return (moduleFactory?.[SYM_ORIGINAL_FACTORY] ?? moduleFactory) as AnyModuleFactory | undefined; -} +// wreq.m is the Webpack object containing module factories. It is pre-populated with factories, and is also populated via webpackGlobal.push +// We use this setter to intercept when wreq.m is defined and apply patching to its factories. -export function getFactoryPatchedSource(id: PropertyKey, webpackRequire = wreq as AnyWebpackRequire) { - return webpackRequire.m[id]?.[SYM_PATCHED_SOURCE]; -} +// Factories can be patched in two ways. Eagerly or lazily. +// If we are patching eagerly, pre-populated factories are patched immediately and new factories are patched when set. +// Else, we only patch them when called. -export function getFactoryPatchedBy(id: PropertyKey, webpackRequire = wreq as AnyWebpackRequire) { - return webpackRequire.m[id]?.[SYM_PATCHED_BY]; -} +// Factories are always wrapped in a proxy, which allows us to intercept the call to them, patch if they werent eagerly patched, +// and call them with our wrapper which notifies our listeners. -// wreq.m is the Webpack object containing module factories. It is pre-populated with module factories, and is also populated via webpackGlobal.push -// We use this setter to intercept when wreq.m is defined and apply the patching in its module factories. -// We wrap wreq.m with our proxy, which is responsible for patching the module factories when they are set, or defining getters for the patched versions. +// wreq.m is also wrapped in a proxy to intercept when new factories are set, patch them eargely, if enabled, and wrap them in the factory proxy. // If this is the main Webpack, we also set up the internal references to WebpackRequire. define(Function.prototype, "m", { @@ -131,13 +127,17 @@ define(Function.prototype, "m", { const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "e"), 0); // Patch the pre-populated factories - for (const id in originalModules) { - if (updateExistingFactory(originalModules, id, originalModules[id], true)) { + for (const moduleId in originalModules) { + const originalFactory = originalModules[moduleId]; + + if (updateExistingFactory(originalModules, moduleId, originalFactory, originalModules, true)) { continue; } - notifyFactoryListeners(originalModules[id]); - defineModulesFactoryGetter(id, Settings.eagerPatches && shouldPatchFactories ? wrapAndPatchFactory(id, originalModules[id]) : originalModules[id]); + notifyFactoryListeners(moduleId, originalFactory); + + const proxiedFactory = new Proxy(Settings.eagerPatches ? patchFactory(moduleId, originalFactory) : originalFactory, moduleFactoryHandler); + define(originalModules, moduleId, { value: proxiedFactory }); } define(originalModules, Symbol.toStringTag, { @@ -145,7 +145,6 @@ define(Function.prototype, "m", { enumerable: false }); - // The proxy responsible for patching the module factories when they are set, or defining getters for the patched versions const proxiedModuleFactories = new Proxy(originalModules, moduleFactoriesHandler); /* If Webpack ever decides to set module factories using the variable of the modules object directly, instead of wreq.m, switch the proxy to the prototype @@ -156,6 +155,7 @@ define(Function.prototype, "m", { } }); +// The proxy for patching eagerly and/or wrapping factories in their proxy. const moduleFactoriesHandler: ProxyHandler = { /* If Webpack ever decides to set module factories using the variable of the modules object directly instead of wreq.m, we need to switch the proxy to the prototype @@ -172,57 +172,96 @@ const moduleFactoriesHandler: ProxyHandler = { }, */ - // The set trap for patching or defining getters for the module factories when new module factories are loaded set(target, p, newValue, receiver) { - if (updateExistingFactory(target, p, newValue)) { + if (updateExistingFactory(target, p, newValue, receiver)) { return true; } - notifyFactoryListeners(newValue); - defineModulesFactoryGetter(p, Settings.eagerPatches && shouldPatchFactories ? wrapAndPatchFactory(p, newValue) : newValue); + notifyFactoryListeners(p, newValue); - return true; + const proxiedFactory = new Proxy(Settings.eagerPatches ? patchFactory(p, newValue) : newValue, moduleFactoryHandler); + return Reflect.set(target, p, proxiedFactory, receiver); + } +}; + +// The proxy for patching lazily and/or running factories with our wrapper. +const moduleFactoryHandler: ProxyHandler = { + apply(target, thisArg: unknown, argArray: Parameters) { + // SAFETY: Factories have `name` as their key in the module factories object, and that is always their module id + const moduleId = target.name; + + // SYM_ORIGINAL_FACTORY means the factory has already been patched + if (target[SYM_ORIGINAL_FACTORY] != null) { + return runFactoryWithWrap(moduleId, target as PatchedModuleFactory, thisArg, argArray); + } + + const patchedFactory = patchFactory(moduleId, target); + return runFactoryWithWrap(moduleId, patchedFactory, thisArg, argArray); + }, + + get(target, p, receiver) { + if (target[SYM_ORIGINAL_FACTORY] != null && (p === SYM_PATCHED_SOURCE || p === SYM_PATCHED_BY)) { + return Reflect.get(target[SYM_ORIGINAL_FACTORY], p, target[SYM_ORIGINAL_FACTORY]); + } + + const v = Reflect.get(target, p, receiver); + + // Make proxied factories `toString` return their original factory `toString` + if (p === "toString") { + return v.bind(target[SYM_ORIGINAL_FACTORY] ?? target); + } + + return v; } }; /** * Update a factory that exists in any Webpack instance with a new original factory. * - * @target The module factories where this new original factory is being set - * @param id The id of the module + * @param moduleFactoriesTarget The module factories where this new original factory is being set + * @param moduleId The id of the module * @param newFactory The new original factory + * @param receiver The receiver of the new factory * @param ignoreExistingInTarget Whether to ignore checking if the factory already exists in the moduleFactoriesTarget * @returns Whether the original factory was updated, or false if it doesn't exist in any Webpack instance */ -function updateExistingFactory(moduleFactoriesTarget: AnyWebpackRequire["m"], id: PropertyKey, newFactory: AnyModuleFactory, ignoreExistingInTarget: boolean = false) { +function updateExistingFactory(moduleFactoriesTarget: AnyWebpackRequire["m"], moduleId: PropertyKey, newFactory: AnyModuleFactory, receiver: any, ignoreExistingInTarget: boolean = false) { let existingFactory: TypedPropertyDescriptor | undefined; let moduleFactoriesWithFactory: AnyWebpackRequire["m"] | undefined; for (const wreq of allWebpackInstances) { - if (ignoreExistingInTarget && wreq.m === moduleFactoriesTarget) continue; + if (ignoreExistingInTarget && wreq.m === moduleFactoriesTarget) { + continue; + } - if (Object.hasOwn(wreq.m, id)) { - existingFactory = Reflect.getOwnPropertyDescriptor(wreq.m, id); + if (Object.hasOwn(wreq.m, moduleId)) { + existingFactory = Reflect.getOwnPropertyDescriptor(wreq.m, moduleId); moduleFactoriesWithFactory = wreq.m; break; } } if (existingFactory != null) { - // If existingFactory exists in any Webpack instance, it's either wrapped in defineModuleFactoryGetter, or it has already been required. - // So define the descriptor of it on this current Webpack instance (if it doesn't exist already), call Reflect.set with the new original, - // and let the correct logic apply (normal set, or defineModuleFactoryGetter setter) - + // If existingFactory exists in any Webpack instance, it's either wrapped in our proxy, or it has already been required. + // In the case it is wrapped in our proxy, we need the Webpack instance with this new original factory to also have our proxy. + // So, define the descriptor of the existing factory on it. if (moduleFactoriesWithFactory !== moduleFactoriesTarget) { - Reflect.defineProperty(moduleFactoriesTarget, id, existingFactory); + Reflect.defineProperty(receiver, moduleId, existingFactory); } - // Persist patched source and patched by in the new original factory, if the patched one has already been required - if (IS_DEV && existingFactory.value != null) { - newFactory[SYM_PATCHED_SOURCE] = existingFactory.value[SYM_PATCHED_SOURCE]; - newFactory[SYM_PATCHED_BY] = existingFactory.value[SYM_PATCHED_BY]; + const existingFactoryValue = moduleFactoriesWithFactory![moduleId]; + + // Update with the new original factory, if it does have a current original factory + if (existingFactoryValue[SYM_ORIGINAL_FACTORY] != null) { + existingFactoryValue[SYM_ORIGINAL_FACTORY] = newFactory; } - return Reflect.set(moduleFactoriesTarget, id, newFactory, moduleFactoriesTarget); + // Persist patched source and patched by in the new original factory + if (IS_DEV) { + newFactory[SYM_PATCHED_SOURCE] = existingFactoryValue[SYM_PATCHED_SOURCE]; + newFactory[SYM_PATCHED_BY] = existingFactoryValue[SYM_PATCHED_BY]; + } + + return true; } return false; @@ -231,12 +270,13 @@ function updateExistingFactory(moduleFactoriesTarget: AnyWebpackRequire["m"], id /** * Notify all factory listeners. * + * @param moduleId The id of the module * @param factory The original factory to notify for */ -function notifyFactoryListeners(factory: AnyModuleFactory) { +function notifyFactoryListeners(moduleId: PropertyKey, factory: AnyModuleFactory) { for (const factoryListener of factoryListeners) { try { - factoryListener(factory); + factoryListener(factory, moduleId); } catch (err) { logger.error("Error in Webpack factory listener:\n", err, factoryListener); } @@ -244,190 +284,138 @@ function notifyFactoryListeners(factory: AnyModuleFactory) { } /** - * Define the getter for returning the patched version of the module factory. + * Run a (possibly) patched module factory with a wrapper which notifies our listeners. * - * If eagerPatches is enabled, the factory argument should already be the patched version, else it will be the original - * and only be patched when accessed for the first time. - * - * @param id The id of the module - * @param factory The original or patched module factory + * @param moduleId The id of the module + * @param patchedFactory The (possibly) patched module factory + * @param thisArg The `value` of the call to the factory + * @param argArray The arguments of the call to the factory */ -function defineModulesFactoryGetter(id: PropertyKey, factory: MaybeWrappedModuleFactory) { - const descriptor: PropertyDescriptor = { - get() { - // SYM_ORIGINAL_FACTORY means the factory is already patched - if (!shouldPatchFactories || factory[SYM_ORIGINAL_FACTORY] != null) { - return factory; - } +function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModuleFactory, thisArg: unknown, argArray: Parameters) { + const originalFactory = patchedFactory[SYM_ORIGINAL_FACTORY]; - return (factory = wrapAndPatchFactory(id, factory)); - }, - set(newFactory: MaybeWrappedModuleFactory) { - if (IS_DEV) { - newFactory[SYM_PATCHED_SOURCE] = factory[SYM_PATCHED_SOURCE]; - newFactory[SYM_PATCHED_BY] = factory[SYM_PATCHED_BY]; - } - - if (factory[SYM_ORIGINAL_FACTORY] != null) { - factory.toString = newFactory.toString.bind(newFactory); - factory[SYM_ORIGINAL_FACTORY] = newFactory; - } else { - factory = newFactory; - } - } - }; - - // Define the getter in all the module factories objects. Patches are only executed once, so make sure all module factories object - // have the patched version - for (const wreq of allWebpackInstances) { - define(wreq.m, id, descriptor); + if (patchedFactory === originalFactory) { + // @ts-expect-error Clear up ORIGINAL_FACTORY if the factory did not have any patch applied + delete patchedFactory[SYM_ORIGINAL_FACTORY]; } -} -/** - * Wraps and patches a module factory. - * - * @param id The id of the module - * @param factory The original or patched module factory - * @returns The wrapper for the patched module factory - */ -function wrapAndPatchFactory(id: PropertyKey, originalFactory: AnyModuleFactory) { - const [patchedFactory, patchedSource, patchedBy] = patchFactory(id, originalFactory); + // Restore the original factory in all the module factories objects, discarding our proxy and allowing it to be garbage collected + for (const wreq of allWebpackInstances) { + define(wreq.m, moduleId, { value: originalFactory }); + } - const wrappedFactory: WrappedModuleFactory = function (...args) { - // Restore the original factory in all the module factories objects. We want to make sure the original factory is restored properly, no matter what is the Webpack instance - for (const wreq of allWebpackInstances) { - define(wreq.m, id, { value: wrappedFactory[SYM_ORIGINAL_FACTORY] }); - } + let [module, exports, require] = argArray; - // eslint-disable-next-line prefer-const - let [module, exports, require] = args; + if (wreq == null) { + if (!wreqFallbackApplied) { + wreqFallbackApplied = true; - if (wreq == null) { - if (!wreqFallbackApplied) { - wreqFallbackApplied = true; + // Make sure the require argument is actually the WebpackRequire function + if (typeof require === "function" && require.m != null) { + const { stack } = new Error(); + const webpackInstanceFileName = stack?.match(/\/assets\/(.+?\.js)/)?.[1]; - // Make sure the require argument is actually the WebpackRequire function - if (typeof require === "function" && require.m != null) { - const { stack } = new Error(); - const webpackInstanceFileName = stack?.match(/\/assets\/(.+?\.js)/)?.[1]; + logger.warn( + "WebpackRequire was not initialized, falling back to WebpackRequire passed to the first called wrapped module factory (" + + `id: ${String(moduleId)}` + interpolateIfDefined`, WebpackInstance origin: ${webpackInstanceFileName}` + + ")" + ); - logger.warn( - "WebpackRequire was not initialized, falling back to WebpackRequire passed to the first called patched module factory (" + - `id: ${String(id)}` + interpolateIfDefined`, WebpackInstance origin: ${webpackInstanceFileName}` + - ")" - ); - - _initWebpack(require as WebpackRequire); - } else if (IS_DEV) { - logger.error("WebpackRequire was not initialized, running modules without patches instead."); - return wrappedFactory[SYM_ORIGINAL_FACTORY].apply(this, args); - } + // Could technically be wrong, but it's better than nothing + _initWebpack(require as WebpackRequire); } else if (IS_DEV) { - return wrappedFactory[SYM_ORIGINAL_FACTORY].apply(this, args); + logger.error("WebpackRequire was not initialized, running modules without patches instead."); + return originalFactory.apply(thisArg, argArray); } + } else if (IS_DEV) { + return originalFactory.apply(thisArg, argArray); + } + } + + let factoryReturn: unknown; + try { + factoryReturn = patchedFactory.apply(thisArg, argArray); + } catch (err) { + // Just re-throw Discord errors + if (patchedFactory === originalFactory) { + throw err; } - let factoryReturn: unknown; - try { - // Call the patched factory - factoryReturn = patchedFactory.apply(this, args); - } catch (err) { - // Just re-throw Discord errors - if (patchedFactory === wrappedFactory[SYM_ORIGINAL_FACTORY]) { - throw err; + logger.error("Error in patched module factory:\n", err); + return originalFactory.apply(thisArg, argArray); + } + + exports = module.exports; + if (exports == null) { + return factoryReturn; + } + + if (typeof require === "function") { + const shouldIgnoreModule = _shouldIgnoreModule(exports); + + if (shouldIgnoreModule) { + if (require.c != null) { + Object.defineProperty(require.c, moduleId, { + value: require.c[moduleId], + enumerable: false, + configurable: true, + writable: true + }); } - logger.error("Error in patched module factory:\n", err); - return wrappedFactory[SYM_ORIGINAL_FACTORY].apply(this, args); - } - - exports = module.exports; - if (exports == null) { return factoryReturn; } - - if (typeof require === "function") { - const shouldIgnoreModule = _shouldIgnoreModule(exports); - - if (shouldIgnoreModule) { - if (require.c != null) { - Object.defineProperty(require.c, id, { - value: require.c[id], - enumerable: false, - configurable: true, - writable: true - }); - } - - return factoryReturn; - } - } - - for (const callback of moduleListeners) { - try { - callback(exports, id); - } catch (err) { - logger.error("Error in Webpack module listener:\n", err, callback); - } - } - - for (const [filter, callback] of waitForSubscriptions) { - try { - if (filter(exports)) { - waitForSubscriptions.delete(filter); - callback(exports, id); - continue; - } - - if (typeof exports !== "object") { - continue; - } - - for (const exportKey in exports) { - const exportValue = exports[exportKey]; - - if (exportValue != null && filter(exportValue)) { - waitForSubscriptions.delete(filter); - callback(exportValue, id); - break; - } - } - } catch (err) { - logger.error("Error while firing callback for Webpack waitFor subscription:\n", err, filter, callback); - } - } - - return factoryReturn; - }; - - wrappedFactory.toString = originalFactory.toString.bind(originalFactory); - wrappedFactory[SYM_ORIGINAL_FACTORY] = originalFactory; - - if (IS_DEV && patchedFactory !== originalFactory) { - wrappedFactory[SYM_PATCHED_SOURCE] = patchedSource; - wrappedFactory[SYM_PATCHED_BY] = patchedBy; - originalFactory[SYM_PATCHED_SOURCE] = patchedSource; - originalFactory[SYM_PATCHED_BY] = patchedBy; } - // @ts-expect-error Allow GC to get into action, if possible - originalFactory = undefined; - return wrappedFactory; + for (const callback of moduleListeners) { + try { + callback(exports, moduleId); + } catch (err) { + logger.error("Error in Webpack module listener:\n", err, callback); + } + } + + for (const [filter, callback] of waitForSubscriptions) { + try { + if (filter(exports)) { + waitForSubscriptions.delete(filter); + callback(exports, moduleId); + continue; + } + + if (typeof exports !== "object") { + continue; + } + + for (const exportKey in exports) { + const exportValue = exports[exportKey]; + + if (exportValue != null && filter(exportValue)) { + waitForSubscriptions.delete(filter); + callback(exportValue, moduleId); + break; + } + } + } catch (err) { + logger.error("Error while firing callback for Webpack waitFor subscription:\n", err, filter, callback); + } + } + + return factoryReturn; } /** * Patches a module factory. * - * @param id The id of the module - * @param factory The original module factory - * @returns The patched module factory, the patched source of it, and the plugins that patched it + * @param moduleId The id of the module + * @param originalFactory The original module factory + * @returns The patched module factory */ -function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFactory: AnyModuleFactory, patchedSource: string, patchedBy: Set] { +function patchFactory(moduleId: PropertyKey, originalFactory: AnyModuleFactory): PatchedModuleFactory { // 0, prefix to turn it into an expression: 0,function(){} would be invalid syntax without the 0, - let code: string = "0," + String(factory); + let code: string = "0," + String(originalFactory); let patchedSource = code; - let patchedFactory = factory; + let patchedFactory = originalFactory; const patchedBy = new Set(); @@ -442,8 +430,8 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto continue; } - // Reporter eagerly patches and cannot retrieve the build number because this code runs before the module for it is loaded - const buildNumber = IS_REPORTER ? -1 : getBuildNumber(); + // Eager patches cannot retrieve the build number because this code runs before the module for it is loaded + const buildNumber = Settings.eagerPatches ? -1 : getBuildNumber(); const shouldCheckBuildNumber = !Settings.eagerPatches && buildNumber !== -1; if ( @@ -463,7 +451,7 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto }); const previousCode = code; - const previousFactory = factory; + const previousFactory = originalFactory; let markedAsPatched = false; // We change all patch.replacement to array in plugins/index @@ -482,18 +470,18 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto } const lastCode = code; - const lastFactory = factory; + const lastFactory = originalFactory; try { const [newCode, totalTime] = executePatch(replacement.match, replacement.replace as string); if (IS_REPORTER) { - patchTimings.push([patch.plugin, id, replacement.match, totalTime]); + patchTimings.push([patch.plugin, moduleId, replacement.match, totalTime]); } if (newCode === code) { if (!patch.noWarn) { - logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${String(id)}): ${replacement.match}`); + logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${String(moduleId)}): ${replacement.match}`); if (IS_DEV) { logger.debug("Function Source:\n", code); } @@ -515,7 +503,7 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto } code = newCode; - patchedSource = `// Webpack Module ${String(id)} - Patched by ${[...patchedBy, patch.plugin].join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${String(id)}`; + patchedSource = `// Webpack Module ${String(moduleId)} - Patched by ${[...patchedBy, patch.plugin].join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${String(moduleId)}`; patchedFactory = (0, eval)(patchedSource); if (!patchedBy.has(patch.plugin)) { @@ -523,7 +511,7 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto markedAsPatched = true; } } catch (err) { - logger.error(`Patch by ${patch.plugin} errored (Module id is ${String(id)}): ${replacement.match}\n`, err); + logger.error(`Patch by ${patch.plugin} errored (Module id is ${String(moduleId)}): ${replacement.match}\n`, err); if (IS_DEV) { diffErroredPatch(code, lastCode, lastCode.match(replacement.match)!); @@ -550,7 +538,14 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory): [patchedFacto } } - return [patchedFactory, patchedSource, patchedBy]; + patchedFactory[SYM_ORIGINAL_FACTORY] = originalFactory; + + if (IS_DEV && patchedFactory !== originalFactory) { + originalFactory[SYM_PATCHED_SOURCE] = patchedSource; + originalFactory[SYM_PATCHED_BY] = patchedBy; + } + + return patchedFactory as PatchedModuleFactory; } function diffErroredPatch(code: string, lastCode: string, match: RegExpMatchArray) { diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index 8d5b3c68..09c04a2a 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -90,7 +90,7 @@ export const filters = { }; export type CallbackFn = (module: ModuleExports, id: PropertyKey) => void; -export type FactoryListernFn = (factory: AnyModuleFactory) => void; +export type FactoryListernFn = (factory: AnyModuleFactory, moduleId: PropertyKey) => void; export const waitForSubscriptions = new Map(); export const moduleListeners = new Set(); diff --git a/src/webpack/wreq.d.ts b/src/webpack/wreq.d.ts index dbc45105..b9f5353f 100644 --- a/src/webpack/wreq.d.ts +++ b/src/webpack/wreq.d.ts @@ -200,12 +200,10 @@ export type AnyModuleFactory = ((this: ModuleExports, module: Module, exports: M [SYM_PATCHED_BY]?: Set; }; -export type WrappedModuleFactory = AnyModuleFactory & { +export type PatchedModuleFactory = AnyModuleFactory & { [SYM_ORIGINAL_FACTORY]: AnyModuleFactory; [SYM_PATCHED_SOURCE]?: string; [SYM_PATCHED_BY]?: Set; }; -export type MaybeWrappedModuleFactory = AnyModuleFactory | WrappedModuleFactory; - -export type WrappedModuleFactories = Record; +export type MaybePatchedModuleFactory = PatchedModuleFactory | AnyModuleFactory; diff --git a/tsconfig.json b/tsconfig.json index db6d0918..d2a42bd5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -29,7 +29,9 @@ "@shared/*": ["./shared/*"], "@webpack/types": ["./webpack/common/types"], "@webpack/common": ["./webpack/common"], - "@webpack": ["./webpack/webpack"] + "@webpack": ["./webpack/webpack"], + "@webpack/patcher": ["./webpack/patchWebpack"], + "@webpack/wreq.d": ["./webpack/wreq.d"], }, "plugins": [ From 5196c2adc0fe348d66dacde7e04d3247f58dc073 Mon Sep 17 00:00:00 2001 From: v Date: Wed, 12 Feb 2025 18:09:14 +0100 Subject: [PATCH 02/34] Update esbuild to latest version (#3218) --- browser/manifest.json | 2 +- browser/monaco.ts | 2 +- browser/monacoWin.html | 4 +- package.json | 5 +- pnpm-lock.yaml | 482 +++++++++++++++------------- scripts/build/build.mjs | 99 +++--- scripts/build/buildWeb.mjs | 138 ++++---- scripts/build/common.mjs | 43 ++- scripts/build/tsconfig.esbuild.json | 7 - 9 files changed, 406 insertions(+), 376 deletions(-) delete mode 100644 scripts/build/tsconfig.esbuild.json diff --git a/browser/manifest.json b/browser/manifest.json index 357312b0..3463e46c 100644 --- a/browser/manifest.json +++ b/browser/manifest.json @@ -36,7 +36,7 @@ "web_accessible_resources": [ { - "resources": ["dist/*", "third-party/*"], + "resources": ["dist/*", "vendor/*"], "matches": ["*://*.discord.com/*"] } ], diff --git a/browser/monaco.ts b/browser/monaco.ts index ead061d6..dc243df7 100644 --- a/browser/monaco.ts +++ b/browser/monaco.ts @@ -15,7 +15,7 @@ declare global { const getTheme: () => string; } -const BASE = "/dist/monaco/vs"; +const BASE = "/vendor/monaco/vs"; self.MonacoEnvironment = { getWorkerUrl(_moduleId: unknown, label: string) { diff --git a/browser/monacoWin.html b/browser/monacoWin.html index a55b0e54..12523d45 100644 --- a/browser/monacoWin.html +++ b/browser/monacoWin.html @@ -24,12 +24,12 @@ diff --git a/package.json b/package.json index dca52a16..872d7ce0 100644 --- a/package.json +++ b/package.json @@ -30,13 +30,12 @@ "lint": "eslint", "lint-styles": "stylelint \"src/**/*.css\" --ignore-pattern src/userplugins", "lint:fix": "pnpm lint --fix", - "test": "pnpm buildStandalone && pnpm lint && pnpm lint-styles && pnpm testTsc && pnpm generatePluginJson", + "test": "pnpm buildStandalone && pnpm testTsc && pnpm lint && pnpm lint-styles && pnpm generatePluginJson", "testWeb": "pnpm lint && pnpm buildWeb && pnpm testTsc", "testTsc": "tsc --noEmit" }, "dependencies": { "@intrnl/xxhash64": "^0.1.2", - "@sapphi-red/web-noise-suppressor": "0.3.5", "@vap/core": "0.0.12", "@vap/shiki": "0.10.5", "fflate": "^0.8.2", @@ -56,7 +55,7 @@ "@types/yazl": "^2.4.5", "diff": "^7.0.0", "discord-types": "^1.3.26", - "esbuild": "^0.15.18", + "esbuild": "^0.25.0", "eslint": "^9.17.0", "eslint-import-resolver-alias": "^1.1.2", "eslint-plugin-path-alias": "2.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a49df467..169d76fc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,9 +19,6 @@ importers: '@intrnl/xxhash64': specifier: ^0.1.2 version: 0.1.2 - '@sapphi-red/web-noise-suppressor': - specifier: 0.3.5 - version: 0.3.5 '@vap/core': specifier: 0.0.12 version: 0.0.12 @@ -75,8 +72,8 @@ importers: specifier: ^1.3.26 version: 1.3.26 esbuild: - specifier: ^0.15.18 - version: 0.15.18 + specifier: ^0.25.0 + version: 0.25.0 eslint: specifier: ^9.17.0 version: 9.17.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4) @@ -229,6 +226,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.25.0': + resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.17.19': resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} engines: {node: '>=12'} @@ -241,10 +244,10 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm@0.15.18': - resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==} - engines: {node: '>=12'} - cpu: [arm] + '@esbuild/android-arm64@0.25.0': + resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} + engines: {node: '>=18'} + cpu: [arm64] os: [android] '@esbuild/android-arm@0.17.19': @@ -259,6 +262,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.25.0': + resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.17.19': resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==} engines: {node: '>=12'} @@ -271,6 +280,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.25.0': + resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.17.19': resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==} engines: {node: '>=12'} @@ -283,6 +298,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.25.0': + resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.17.19': resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==} engines: {node: '>=12'} @@ -295,6 +316,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.25.0': + resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.17.19': resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==} engines: {node: '>=12'} @@ -307,6 +334,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.25.0': + resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.17.19': resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==} engines: {node: '>=12'} @@ -319,6 +352,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.25.0': + resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.17.19': resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==} engines: {node: '>=12'} @@ -331,6 +370,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.25.0': + resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.17.19': resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==} engines: {node: '>=12'} @@ -343,6 +388,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.25.0': + resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.17.19': resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==} engines: {node: '>=12'} @@ -355,10 +406,10 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.15.18': - resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==} - engines: {node: '>=12'} - cpu: [loong64] + '@esbuild/linux-ia32@0.25.0': + resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} + engines: {node: '>=18'} + cpu: [ia32] os: [linux] '@esbuild/linux-loong64@0.17.19': @@ -373,6 +424,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.25.0': + resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.17.19': resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==} engines: {node: '>=12'} @@ -385,6 +442,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.25.0': + resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.17.19': resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==} engines: {node: '>=12'} @@ -397,6 +460,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.25.0': + resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.17.19': resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==} engines: {node: '>=12'} @@ -409,6 +478,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.25.0': + resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.17.19': resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} engines: {node: '>=12'} @@ -421,6 +496,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.25.0': + resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.17.19': resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==} engines: {node: '>=12'} @@ -433,6 +514,18 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.25.0': + resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.0': + resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.17.19': resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==} engines: {node: '>=12'} @@ -445,12 +538,24 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.25.0': + resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.23.1': resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.25.0': + resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.17.19': resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==} engines: {node: '>=12'} @@ -463,6 +568,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.0': + resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/sunos-x64@0.17.19': resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==} engines: {node: '>=12'} @@ -475,6 +586,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.25.0': + resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.17.19': resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==} engines: {node: '>=12'} @@ -487,6 +604,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.25.0': + resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.17.19': resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==} engines: {node: '>=12'} @@ -499,6 +622,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.25.0': + resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.17.19': resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==} engines: {node: '>=12'} @@ -511,6 +640,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.25.0': + resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.4.1': resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -609,9 +744,6 @@ packages: '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - '@sapphi-red/web-noise-suppressor@0.3.5': - resolution: {integrity: sha512-jh3+V9yM+zxLriQexoGm0GatoPaJWjs6ypFIbFYwQp+AoUb55eUXrjKtKQyuC5zShzzeAQUl0M5JzqB7SSrsRA==} - '@stylistic/eslint-plugin@2.12.1': resolution: {integrity: sha512-fubZKIHSPuo07FgRTn6S4Nl0uXPRPYVNpyZzIDGfp7Fny6JjNus6kReLD7NI380JXi4HtUTSOZ34LBuNPO1XLQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1205,131 +1337,6 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - esbuild-android-64@0.15.18: - resolution: {integrity: sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - esbuild-android-arm64@0.15.18: - resolution: {integrity: sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - esbuild-darwin-64@0.15.18: - resolution: {integrity: sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - esbuild-darwin-arm64@0.15.18: - resolution: {integrity: sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - esbuild-freebsd-64@0.15.18: - resolution: {integrity: sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - esbuild-freebsd-arm64@0.15.18: - resolution: {integrity: sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - esbuild-linux-32@0.15.18: - resolution: {integrity: sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - esbuild-linux-64@0.15.18: - resolution: {integrity: sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - - esbuild-linux-arm64@0.15.18: - resolution: {integrity: sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - esbuild-linux-arm@0.15.18: - resolution: {integrity: sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - esbuild-linux-mips64le@0.15.18: - resolution: {integrity: sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - esbuild-linux-ppc64le@0.15.18: - resolution: {integrity: sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - esbuild-linux-riscv64@0.15.18: - resolution: {integrity: sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - esbuild-linux-s390x@0.15.18: - resolution: {integrity: sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - esbuild-netbsd-64@0.15.18: - resolution: {integrity: sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - - esbuild-openbsd-64@0.15.18: - resolution: {integrity: sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - - esbuild-sunos-64@0.15.18: - resolution: {integrity: sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - - esbuild-windows-32@0.15.18: - resolution: {integrity: sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - esbuild-windows-64@0.15.18: - resolution: {integrity: sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - - esbuild-windows-arm64@0.15.18: - resolution: {integrity: sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - esbuild@0.15.18: - resolution: {integrity: sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==} - engines: {node: '>=12'} - hasBin: true - esbuild@0.17.19: resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} engines: {node: '>=12'} @@ -1340,6 +1347,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.25.0: + resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -2889,13 +2901,16 @@ snapshots: '@esbuild/aix-ppc64@0.23.1': optional: true + '@esbuild/aix-ppc64@0.25.0': + optional: true + '@esbuild/android-arm64@0.17.19': optional: true '@esbuild/android-arm64@0.23.1': optional: true - '@esbuild/android-arm@0.15.18': + '@esbuild/android-arm64@0.25.0': optional: true '@esbuild/android-arm@0.17.19': @@ -2904,55 +2919,79 @@ snapshots: '@esbuild/android-arm@0.23.1': optional: true + '@esbuild/android-arm@0.25.0': + optional: true + '@esbuild/android-x64@0.17.19': optional: true '@esbuild/android-x64@0.23.1': optional: true + '@esbuild/android-x64@0.25.0': + optional: true + '@esbuild/darwin-arm64@0.17.19': optional: true '@esbuild/darwin-arm64@0.23.1': optional: true + '@esbuild/darwin-arm64@0.25.0': + optional: true + '@esbuild/darwin-x64@0.17.19': optional: true '@esbuild/darwin-x64@0.23.1': optional: true + '@esbuild/darwin-x64@0.25.0': + optional: true + '@esbuild/freebsd-arm64@0.17.19': optional: true '@esbuild/freebsd-arm64@0.23.1': optional: true + '@esbuild/freebsd-arm64@0.25.0': + optional: true + '@esbuild/freebsd-x64@0.17.19': optional: true '@esbuild/freebsd-x64@0.23.1': optional: true + '@esbuild/freebsd-x64@0.25.0': + optional: true + '@esbuild/linux-arm64@0.17.19': optional: true '@esbuild/linux-arm64@0.23.1': optional: true + '@esbuild/linux-arm64@0.25.0': + optional: true + '@esbuild/linux-arm@0.17.19': optional: true '@esbuild/linux-arm@0.23.1': optional: true + '@esbuild/linux-arm@0.25.0': + optional: true + '@esbuild/linux-ia32@0.17.19': optional: true '@esbuild/linux-ia32@0.23.1': optional: true - '@esbuild/linux-loong64@0.15.18': + '@esbuild/linux-ia32@0.25.0': optional: true '@esbuild/linux-loong64@0.17.19': @@ -2961,75 +3000,117 @@ snapshots: '@esbuild/linux-loong64@0.23.1': optional: true + '@esbuild/linux-loong64@0.25.0': + optional: true + '@esbuild/linux-mips64el@0.17.19': optional: true '@esbuild/linux-mips64el@0.23.1': optional: true + '@esbuild/linux-mips64el@0.25.0': + optional: true + '@esbuild/linux-ppc64@0.17.19': optional: true '@esbuild/linux-ppc64@0.23.1': optional: true + '@esbuild/linux-ppc64@0.25.0': + optional: true + '@esbuild/linux-riscv64@0.17.19': optional: true '@esbuild/linux-riscv64@0.23.1': optional: true + '@esbuild/linux-riscv64@0.25.0': + optional: true + '@esbuild/linux-s390x@0.17.19': optional: true '@esbuild/linux-s390x@0.23.1': optional: true + '@esbuild/linux-s390x@0.25.0': + optional: true + '@esbuild/linux-x64@0.17.19': optional: true '@esbuild/linux-x64@0.23.1': optional: true + '@esbuild/linux-x64@0.25.0': + optional: true + + '@esbuild/netbsd-arm64@0.25.0': + optional: true + '@esbuild/netbsd-x64@0.17.19': optional: true '@esbuild/netbsd-x64@0.23.1': optional: true + '@esbuild/netbsd-x64@0.25.0': + optional: true + '@esbuild/openbsd-arm64@0.23.1': optional: true + '@esbuild/openbsd-arm64@0.25.0': + optional: true + '@esbuild/openbsd-x64@0.17.19': optional: true '@esbuild/openbsd-x64@0.23.1': optional: true + '@esbuild/openbsd-x64@0.25.0': + optional: true + '@esbuild/sunos-x64@0.17.19': optional: true '@esbuild/sunos-x64@0.23.1': optional: true + '@esbuild/sunos-x64@0.25.0': + optional: true + '@esbuild/win32-arm64@0.17.19': optional: true '@esbuild/win32-arm64@0.23.1': optional: true + '@esbuild/win32-arm64@0.25.0': + optional: true + '@esbuild/win32-ia32@0.17.19': optional: true '@esbuild/win32-ia32@0.23.1': optional: true + '@esbuild/win32-ia32@0.25.0': + optional: true + '@esbuild/win32-x64@0.17.19': optional: true '@esbuild/win32-x64@0.23.1': optional: true + '@esbuild/win32-x64@0.25.0': + optional: true + '@eslint-community/eslint-utils@4.4.1(eslint@9.17.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4))': dependencies: eslint: 9.17.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4) @@ -3135,8 +3216,6 @@ snapshots: '@rtsao/scc@1.1.0': {} - '@sapphi-red/web-noise-suppressor@0.3.5': {} - '@stylistic/eslint-plugin@2.12.1(eslint@9.17.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4))(typescript@5.7.2)': dependencies: '@typescript-eslint/utils': 8.18.1(eslint@9.17.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4))(typescript@5.7.2) @@ -3936,91 +4015,6 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - esbuild-android-64@0.15.18: - optional: true - - esbuild-android-arm64@0.15.18: - optional: true - - esbuild-darwin-64@0.15.18: - optional: true - - esbuild-darwin-arm64@0.15.18: - optional: true - - esbuild-freebsd-64@0.15.18: - optional: true - - esbuild-freebsd-arm64@0.15.18: - optional: true - - esbuild-linux-32@0.15.18: - optional: true - - esbuild-linux-64@0.15.18: - optional: true - - esbuild-linux-arm64@0.15.18: - optional: true - - esbuild-linux-arm@0.15.18: - optional: true - - esbuild-linux-mips64le@0.15.18: - optional: true - - esbuild-linux-ppc64le@0.15.18: - optional: true - - esbuild-linux-riscv64@0.15.18: - optional: true - - esbuild-linux-s390x@0.15.18: - optional: true - - esbuild-netbsd-64@0.15.18: - optional: true - - esbuild-openbsd-64@0.15.18: - optional: true - - esbuild-sunos-64@0.15.18: - optional: true - - esbuild-windows-32@0.15.18: - optional: true - - esbuild-windows-64@0.15.18: - optional: true - - esbuild-windows-arm64@0.15.18: - optional: true - - esbuild@0.15.18: - optionalDependencies: - '@esbuild/android-arm': 0.15.18 - '@esbuild/linux-loong64': 0.15.18 - esbuild-android-64: 0.15.18 - esbuild-android-arm64: 0.15.18 - esbuild-darwin-64: 0.15.18 - esbuild-darwin-arm64: 0.15.18 - esbuild-freebsd-64: 0.15.18 - esbuild-freebsd-arm64: 0.15.18 - esbuild-linux-32: 0.15.18 - esbuild-linux-64: 0.15.18 - esbuild-linux-arm: 0.15.18 - esbuild-linux-arm64: 0.15.18 - esbuild-linux-mips64le: 0.15.18 - esbuild-linux-ppc64le: 0.15.18 - esbuild-linux-riscv64: 0.15.18 - esbuild-linux-s390x: 0.15.18 - esbuild-netbsd-64: 0.15.18 - esbuild-openbsd-64: 0.15.18 - esbuild-sunos-64: 0.15.18 - esbuild-windows-32: 0.15.18 - esbuild-windows-64: 0.15.18 - esbuild-windows-arm64: 0.15.18 - esbuild@0.17.19: optionalDependencies: '@esbuild/android-arm': 0.17.19 @@ -4073,6 +4067,34 @@ snapshots: '@esbuild/win32-ia32': 0.23.1 '@esbuild/win32-x64': 0.23.1 + esbuild@0.25.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.0 + '@esbuild/android-arm': 0.25.0 + '@esbuild/android-arm64': 0.25.0 + '@esbuild/android-x64': 0.25.0 + '@esbuild/darwin-arm64': 0.25.0 + '@esbuild/darwin-x64': 0.25.0 + '@esbuild/freebsd-arm64': 0.25.0 + '@esbuild/freebsd-x64': 0.25.0 + '@esbuild/linux-arm': 0.25.0 + '@esbuild/linux-arm64': 0.25.0 + '@esbuild/linux-ia32': 0.25.0 + '@esbuild/linux-loong64': 0.25.0 + '@esbuild/linux-mips64el': 0.25.0 + '@esbuild/linux-ppc64': 0.25.0 + '@esbuild/linux-riscv64': 0.25.0 + '@esbuild/linux-s390x': 0.25.0 + '@esbuild/linux-x64': 0.25.0 + '@esbuild/netbsd-arm64': 0.25.0 + '@esbuild/netbsd-x64': 0.25.0 + '@esbuild/openbsd-arm64': 0.25.0 + '@esbuild/openbsd-x64': 0.25.0 + '@esbuild/sunos-x64': 0.25.0 + '@esbuild/win32-arm64': 0.25.0 + '@esbuild/win32-ia32': 0.25.0 + '@esbuild/win32-x64': 0.25.0 + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} diff --git a/scripts/build/build.mjs b/scripts/build/build.mjs index 623f9f94..9c2b4970 100755 --- a/scripts/build/build.mjs +++ b/scripts/build/build.mjs @@ -17,38 +17,41 @@ * along with this program. If not, see . */ -import esbuild from "esbuild"; +// @ts-check + import { readdir } from "fs/promises"; import { join } from "path"; -import { BUILD_TIMESTAMP, commonOpts, exists, globPlugins, IS_DEV, IS_REPORTER, IS_STANDALONE, IS_UPDATER_DISABLED, resolvePluginName, VERSION, commonRendererPlugins, watch } from "./common.mjs"; +import { BUILD_TIMESTAMP, commonOpts, exists, globPlugins, IS_DEV, IS_REPORTER, IS_STANDALONE, IS_UPDATER_DISABLED, resolvePluginName, VERSION, commonRendererPlugins, watch, buildOrWatchAll, stringifyValues } from "./common.mjs"; -const defines = { +const defines = stringifyValues({ IS_STANDALONE, IS_DEV, IS_REPORTER, IS_UPDATER_DISABLED, IS_WEB: false, IS_EXTENSION: false, - VERSION: JSON.stringify(VERSION), + VERSION, BUILD_TIMESTAMP -}; +}); -if (defines.IS_STANDALONE === false) +if (defines.IS_STANDALONE === "false") { // If this is a local build (not standalone), optimize // for the specific platform we're on defines["process.platform"] = JSON.stringify(process.platform); +} /** - * @type {esbuild.BuildOptions} + * @type {import("esbuild").BuildOptions} */ const nodeCommonOpts = { ...commonOpts, + define: defines, format: "cjs", platform: "node", target: ["esnext"], - external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external], - define: defines + // @ts-ignore this is never undefined + external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external] }; const sourceMapFooter = s => watch ? "" : `//# sourceMappingURL=vencord://${s}.js.map`; @@ -102,25 +105,27 @@ const globNativesPlugin = { } }; -await Promise.all([ +/** @type {import("esbuild").BuildOptions[]} */ +const buildConfigs = ([ // Discord Desktop main & renderer & preload - esbuild.build({ + { ...nodeCommonOpts, entryPoints: ["src/main/index.ts"], outfile: "dist/patcher.js", footer: { js: "//# sourceURL=VencordPatcher\n" + sourceMapFooter("patcher") }, sourcemap, - define: { - ...defines, - IS_DISCORD_DESKTOP: true, - IS_VESKTOP: false - }, plugins: [ + // @ts-ignore this is never undefined ...nodeCommonOpts.plugins, globNativesPlugin - ] - }), - esbuild.build({ + ], + define: { + ...defines, + IS_DISCORD_DESKTOP: "true", + IS_VESKTOP: "false" + } + }, + { ...commonOpts, entryPoints: ["src/Vencord.ts"], outfile: "dist/renderer.js", @@ -135,11 +140,11 @@ await Promise.all([ ], define: { ...defines, - IS_DISCORD_DESKTOP: true, - IS_VESKTOP: false + IS_DISCORD_DESKTOP: "true", + IS_VESKTOP: "false" } - }), - esbuild.build({ + }, + { ...nodeCommonOpts, entryPoints: ["src/preload.ts"], outfile: "dist/preload.js", @@ -147,29 +152,29 @@ await Promise.all([ sourcemap, define: { ...defines, - IS_DISCORD_DESKTOP: true, - IS_VESKTOP: false + IS_DISCORD_DESKTOP: "true", + IS_VESKTOP: "false" } - }), + }, // Vencord Desktop main & renderer & preload - esbuild.build({ + { ...nodeCommonOpts, entryPoints: ["src/main/index.ts"], outfile: "dist/vencordDesktopMain.js", footer: { js: "//# sourceURL=VencordDesktopMain\n" + sourceMapFooter("vencordDesktopMain") }, sourcemap, - define: { - ...defines, - IS_DISCORD_DESKTOP: false, - IS_VESKTOP: true - }, plugins: [ ...nodeCommonOpts.plugins, globNativesPlugin - ] - }), - esbuild.build({ + ], + define: { + ...defines, + IS_DISCORD_DESKTOP: "false", + IS_VESKTOP: "true" + } + }, + { ...commonOpts, entryPoints: ["src/Vencord.ts"], outfile: "dist/vencordDesktopRenderer.js", @@ -184,11 +189,11 @@ await Promise.all([ ], define: { ...defines, - IS_DISCORD_DESKTOP: false, - IS_VESKTOP: true + IS_DISCORD_DESKTOP: "false", + IS_VESKTOP: "true" } - }), - esbuild.build({ + }, + { ...nodeCommonOpts, entryPoints: ["src/preload.ts"], outfile: "dist/vencordDesktopPreload.js", @@ -196,14 +201,10 @@ await Promise.all([ sourcemap, define: { ...defines, - IS_DISCORD_DESKTOP: false, - IS_VESKTOP: true + IS_DISCORD_DESKTOP: "false", + IS_VESKTOP: "true" } - }), -]).catch(err => { - console.error("Build failed"); - console.error(err.message); - // make ci fail - if (!commonOpts.watch) - process.exitCode = 1; -}); + } +]); + +await buildOrWatchAll(buildConfigs); diff --git a/scripts/build/buildWeb.mjs b/scripts/build/buildWeb.mjs index deab8661..33168ff9 100644 --- a/scripts/build/buildWeb.mjs +++ b/scripts/build/buildWeb.mjs @@ -17,29 +17,30 @@ * along with this program. If not, see . */ -import esbuild from "esbuild"; +// @ts-check + import { readFileSync } from "fs"; import { appendFile, mkdir, readdir, readFile, rm, writeFile } from "fs/promises"; import { join } from "path"; import Zip from "zip-local"; -import { BUILD_TIMESTAMP, commonOpts, globPlugins, IS_DEV, IS_REPORTER, VERSION, commonRendererPlugins } from "./common.mjs"; +import { BUILD_TIMESTAMP, commonOpts, globPlugins, IS_DEV, IS_REPORTER, VERSION, commonRendererPlugins, buildOrWatchAll, stringifyValues } from "./common.mjs"; /** - * @type {esbuild.BuildOptions} + * @type {import("esbuild").BuildOptions} */ const commonOptions = { ...commonOpts, entryPoints: ["browser/Vencord.ts"], - globalName: "Vencord", format: "iife", + globalName: "Vencord", external: ["~plugins", "~git-hash", "/assets/*"], + target: ["esnext"], plugins: [ globPlugins("web"), ...commonRendererPlugins ], - target: ["esnext"], - define: { + define: stringifyValues({ IS_WEB: true, IS_EXTENSION: false, IS_STANDALONE: true, @@ -48,9 +49,9 @@ const commonOptions = { IS_DISCORD_DESKTOP: false, IS_VESKTOP: false, IS_UPDATER_DISABLED: true, - VERSION: JSON.stringify(VERSION), + VERSION, BUILD_TIMESTAMP - } + }) }; const MonacoWorkerEntryPoints = [ @@ -58,70 +59,59 @@ const MonacoWorkerEntryPoints = [ "vs/editor/editor.worker.js" ]; -const RnNoiseFiles = [ - "dist/rnnoise.wasm", - "dist/rnnoise_simd.wasm", - "dist/rnnoise/workletProcessor.js", - "LICENSE" +/** @type {import("esbuild").BuildOptions[]} */ +const buildConfigs = [ + { + entryPoints: MonacoWorkerEntryPoints.map(entry => `node_modules/monaco-editor/esm/${entry}`), + bundle: true, + minify: true, + format: "iife", + outbase: "node_modules/monaco-editor/esm/", + outdir: "dist/vendor/monaco" + }, + { + entryPoints: ["browser/monaco.ts"], + bundle: true, + minify: true, + format: "iife", + outfile: "dist/vendor/monaco/index.js", + loader: { + ".ttf": "file" + } + }, + { + ...commonOptions, + outfile: "dist/browser.js", + footer: { js: "//# sourceURL=VencordWeb" } + }, + { + ...commonOptions, + outfile: "dist/extension.js", + define: { + ...commonOptions.define, + IS_EXTENSION: "true" + }, + footer: { js: "//# sourceURL=VencordWeb" } + }, + { + ...commonOptions, + inject: ["browser/GMPolyfill.js", ...(commonOptions?.inject || [])], + define: { + ...commonOptions.define, + window: "unsafeWindow", + }, + outfile: "dist/Vencord.user.js", + banner: { + js: readFileSync("browser/userscript.meta.js", "utf-8").replace("%version%", `${VERSION}.${new Date().getTime()}`) + }, + footer: { + // UserScripts get wrapped in an iife, so define Vencord prop on window that returns our local + js: "Object.defineProperty(unsafeWindow,'Vencord',{get:()=>Vencord});" + } + } ]; -await Promise.all( - [ - esbuild.build({ - entryPoints: MonacoWorkerEntryPoints.map(entry => `node_modules/monaco-editor/esm/${entry}`), - bundle: true, - minify: true, - format: "iife", - outbase: "node_modules/monaco-editor/esm/", - outdir: "dist/monaco" - }), - esbuild.build({ - entryPoints: ["browser/monaco.ts"], - bundle: true, - minify: true, - format: "iife", - outfile: "dist/monaco/index.js", - loader: { - ".ttf": "file" - } - }), - esbuild.build({ - ...commonOptions, - outfile: "dist/browser.js", - footer: { js: "//# sourceURL=VencordWeb" } - }), - esbuild.build({ - ...commonOptions, - outfile: "dist/extension.js", - define: { - ...commonOptions?.define, - IS_EXTENSION: true, - }, - footer: { js: "//# sourceURL=VencordWeb" } - }), - esbuild.build({ - ...commonOptions, - inject: ["browser/GMPolyfill.js", ...(commonOptions?.inject || [])], - define: { - ...(commonOptions?.define), - window: "unsafeWindow", - }, - outfile: "dist/Vencord.user.js", - banner: { - js: readFileSync("browser/userscript.meta.js", "utf-8").replace("%version%", `${VERSION}.${new Date().getTime()}`) - }, - footer: { - // UserScripts get wrapped in an iife, so define Vencord prop on window that returns our local - js: "Object.defineProperty(unsafeWindow,'Vencord',{get:()=>Vencord});" - } - }) - ] -).catch(err => { - console.error("Build failed"); - console.error(err.message); - if (!commonOpts.watch) - process.exit(1); -});; +await buildOrWatchAll(buildConfigs); /** * @type {(dir: string) => Promise} @@ -155,16 +145,13 @@ async function buildExtension(target, files) { const entries = { "dist/Vencord.js": await readFile("dist/extension.js"), "dist/Vencord.css": await readFile("dist/extension.css"), - ...await loadDir("dist/monaco"), - ...Object.fromEntries(await Promise.all(RnNoiseFiles.map(async file => - [`third-party/rnnoise/${file.replace(/^dist\//, "")}`, await readFile(`node_modules/@sapphi-red/web-noise-suppressor/${file}`)] - ))), + ...await loadDir("dist/vendor/monaco", "dist/"), ...Object.fromEntries(await Promise.all(files.map(async f => { let content = await readFile(join("browser", f)); if (f.startsWith("manifest")) { const json = JSON.parse(content.toString("utf-8")); json.version = VERSION; - content = new TextEncoder().encode(JSON.stringify(json)); + content = Buffer.from(new TextEncoder().encode(JSON.stringify(json))); } return [ @@ -210,7 +197,6 @@ if (!process.argv.includes("--skip-extension")) { Zip.sync.zip("dist/firefox-unpacked").compress().save("dist/extension-firefox.zip"); console.info("Packed Firefox Extension written to dist/extension-firefox.zip"); - } else { await appendCssRuntime; } diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index 5e71c8c5..920e5926 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -16,11 +16,13 @@ * along with this program. If not, see . */ +// @ts-check + import "../suppressExperimentalWarnings.js"; import "../checkNodeVersion.js"; import { exec, execSync } from "child_process"; -import esbuild from "esbuild"; +import esbuild, { build, context } from "esbuild"; import { constants as FsConstants, readFileSync } from "fs"; import { access, readdir, readFile } from "fs/promises"; import { minify as minifyHtml } from "html-minifier-terser"; @@ -31,7 +33,7 @@ import { getPluginTarget } from "../utils.mjs"; import { builtinModules } from "module"; /** @type {import("../../package.json")} */ -const PackageJSON = JSON.parse(readFileSync("package.json")); +const PackageJSON = JSON.parse(readFileSync("package.json", "utf-8")); export const VERSION = PackageJSON.version; // https://reproducible-builds.org/docs/source-date-epoch/ @@ -54,6 +56,34 @@ export const banner = { `.trim() }; +/** + * JSON.stringify all values in an object + * @type {(obj: Record) => Record} + */ +export function stringifyValues(obj) { + for (const key in obj) { + obj[key] = JSON.stringify(obj[key]); + } + return obj; +} + +/** + * @param {import("esbuild").BuildOptions[]} buildConfigs + */ +export async function buildOrWatchAll(buildConfigs) { + if (watch) { + await Promise.all(buildConfigs.map(cfg => + context(cfg).then(ctx => ctx.watch()) + )); + } else { + await Promise.all(buildConfigs.map(cfg => build(cfg))) + .catch(error => { + console.error(error.message); + process.exit(1); // exit immediately to skip the rest of the builds + }); + } +} + const PluginDefinitionNameMatcher = /definePlugin\(\{\s*(["'])?name\1:\s*(["'`])(.+?)\2/; /** * @param {string} base @@ -311,18 +341,16 @@ export const banImportPlugin = (filter, message) => ({ export const commonOpts = { logLevel: "info", bundle: true, - watch, minify: !watch && !IS_REPORTER, - sourcemap: watch ? "inline" : "", + sourcemap: watch ? "inline" : "external", legalComments: "linked", banner, plugins: [fileUrlPlugin, gitHashPlugin, gitRemotePlugin, stylePlugin], external: ["~plugins", "~git-hash", "~git-remote", "/assets/*"], inject: ["./scripts/build/inject/react.mjs"], + jsx: "transform", jsxFactory: "VencordCreateElement", - jsxFragment: "VencordFragment", - // Work around https://github.com/evanw/esbuild/issues/2460 - tsconfig: "./scripts/build/tsconfig.esbuild.json" + jsxFragment: "VencordFragment" }; const escapedBuiltinModules = builtinModules @@ -335,5 +363,6 @@ export const commonRendererPlugins = [ banImportPlugin(/^react$/, "Cannot import from react. React and hooks should be imported from @webpack/common"), banImportPlugin(/^electron(\/.*)?$/, "Cannot import electron in browser code. You need to use a native.ts file"), banImportPlugin(/^ts-pattern$/, "Cannot import from ts-pattern. match and P should be imported from @webpack/common"), + // @ts-ignore this is never undefined ...commonOpts.plugins ]; diff --git a/scripts/build/tsconfig.esbuild.json b/scripts/build/tsconfig.esbuild.json deleted file mode 100644 index e3e28a14..00000000 --- a/scripts/build/tsconfig.esbuild.json +++ /dev/null @@ -1,7 +0,0 @@ -// Work around https://github.com/evanw/esbuild/issues/2460 -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "jsx": "react" - } -} From 146a2ac77d55db354647f65474a31ff8b390ed59 Mon Sep 17 00:00:00 2001 From: v Date: Wed, 12 Feb 2025 19:10:05 +0100 Subject: [PATCH 03/34] reporter: improve display (#3220) --- scripts/generateReport.ts | 132 ++++++++++++++++++++++---------------- 1 file changed, 76 insertions(+), 56 deletions(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 7bfda763..9502d382 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -54,14 +54,17 @@ async function maybeGetError(handle: JSHandle): Promise { .catch(() => undefined); } +interface PatchInfo { + plugin: string; + type: string; + id: string; + match: string; + error?: string; +}; + const report = { - badPatches: [] as { - plugin: string; - type: string; - id: string; - match: string; - error?: string; - }[], + badPatches: [] as PatchInfo[], + slowPatches: [] as PatchInfo[], badStarts: [] as { plugin: string; error: string; @@ -132,53 +135,67 @@ async function printReport() { console.log(); if (process.env.WEBHOOK_URL) { + const patchesToEmbed = (title: string, patches: PatchInfo[], color: number) => ({ + title, + color, + description: patches.map(p => { + const lines = [ + `**__${p.plugin} (${p.type}):__**`, + `ID: \`${p.id}\``, + `Match: ${toCodeBlock(p.match, "Match: ".length, true)}` + ]; + if (p.error) lines.push(`Error: ${toCodeBlock(p.error, "Error: ".length, true)}`); + + return lines.join("\n"); + }).join("\n\n"), + }); + + const embeds = [ + { + author: { + name: `Discord ${CANARY ? "Canary" : "Stable"} (${metaData.buildNumber})`, + url: `https://nelly.tools/builds/app/${metaData.buildHash}`, + icon_url: CANARY ? "https://cdn.discordapp.com/emojis/1252721945699549327.png?size=128" : "https://cdn.discordapp.com/emojis/1252721943463985272.png?size=128" + }, + color: CANARY ? 0xfbb642 : 0x5865f2 + }, + report.badPatches.length > 0 && patchesToEmbed("Bad Patches", report.badPatches, 0xff0000), + report.slowPatches.length > 0 && patchesToEmbed("Slow Patches", report.slowPatches, 0xf0b232), + report.badWebpackFinds.length > 0 && { + title: "Bad Webpack Finds", + description: report.badWebpackFinds.map(f => toCodeBlock(f, 0, true)).join("\n") || "None", + color: 0xff0000 + }, + report.badStarts.length > 0 && { + title: "Bad Starts", + description: report.badStarts.map(p => { + const lines = [ + `**__${p.plugin}:__**`, + toCodeBlock(p.error, 0, true) + ]; + return lines.join("\n"); + } + ).join("\n\n") || "None", + color: 0xff0000 + }, + report.otherErrors.length > 0 && { + title: "Discord Errors", + description: report.otherErrors.length ? toCodeBlock(report.otherErrors.join("\n"), 0, true) : "None", + color: 0xff0000 + } + ].filter(Boolean); + + if (embeds.length === 1) { + embeds.push({ + title: "No issues found", + description: "Seems like everything is working fine (for now) <:shipit:1330992641466433556>", + color: 0x00ff00 + }); + } + const body = JSON.stringify({ username: "Vencord Reporter" + (CANARY ? " (Canary)" : ""), - embeds: [ - { - author: { - name: `Discord ${CANARY ? "Canary" : "Stable"} (${metaData.buildNumber})`, - url: `https://nelly.tools/builds/app/${metaData.buildHash}`, - icon_url: CANARY ? "https://cdn.discordapp.com/emojis/1252721945699549327.png?size=128" : "https://cdn.discordapp.com/emojis/1252721943463985272.png?size=128" - }, - color: CANARY ? 0xfbb642 : 0x5865f2 - }, - { - title: "Bad Patches", - description: report.badPatches.map(p => { - const lines = [ - `**__${p.plugin} (${p.type}):__**`, - `ID: \`${p.id}\``, - `Match: ${toCodeBlock(p.match, "Match: ".length, true)}` - ]; - if (p.error) lines.push(`Error: ${toCodeBlock(p.error, "Error: ".length, true)}`); - return lines.join("\n"); - }).join("\n\n") || "None", - color: report.badPatches.length ? 0xff0000 : 0x00ff00 - }, - { - title: "Bad Webpack Finds", - description: report.badWebpackFinds.map(f => toCodeBlock(f, 0, true)).join("\n") || "None", - color: report.badWebpackFinds.length ? 0xff0000 : 0x00ff00 - }, - { - title: "Bad Starts", - description: report.badStarts.map(p => { - const lines = [ - `**__${p.plugin}:__**`, - toCodeBlock(p.error, 0, true) - ]; - return lines.join("\n"); - } - ).join("\n\n") || "None", - color: report.badStarts.length ? 0xff0000 : 0x00ff00 - }, - { - title: "Discord Errors", - description: report.otherErrors.length ? toCodeBlock(report.otherErrors.join("\n"), 0, true) : "None", - color: report.otherErrors.length ? 0xff0000 : 0x00ff00 - } - ] + embeds }); const headers = { @@ -245,14 +262,17 @@ page.on("console", async e => { switch (tag) { case "WebpackInterceptor:": - const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module|took [\d.]+?ms) \(Module id is (.+?)\): (.+)/)!; - if (!patchFailMatch) break; + 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; + if (!match) break; logStderr(await getText()); process.exitCode = 1; - const [, plugin, type, id, regex] = patchFailMatch; - report.badPatches.push({ + const [, plugin, type, id, regex] = match; + const list = patchFailMatch ? report.badPatches : report.slowPatches; + list.push({ plugin, type, id, From a0d0823deff7268f514616bd75d4b0a2c35cc84b Mon Sep 17 00:00:00 2001 From: vMohammad <62218284+vMohammad24@users.noreply.github.com> Date: Wed, 12 Feb 2025 22:18:51 +0300 Subject: [PATCH 04/34] feat(refactor): fontLoader (#148) --- src/equicordplugins/FontLoader/index.tsx | 96 +++++++++--------------- 1 file changed, 34 insertions(+), 62 deletions(-) diff --git a/src/equicordplugins/FontLoader/index.tsx b/src/equicordplugins/FontLoader/index.tsx index a934acfb..283b9adf 100644 --- a/src/equicordplugins/FontLoader/index.tsx +++ b/src/equicordplugins/FontLoader/index.tsx @@ -13,6 +13,7 @@ import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; import { Card, Forms, React, TextInput } from "@webpack/common"; + interface GoogleFontMetadata { family: string; displayName: string; @@ -27,7 +28,15 @@ interface GoogleFontMetadata { }>; }>; } -const userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36(KHTML, like Gecko) Chrome / 128.0.0.0 Safari / 537.36"; + +const createGoogleFontUrl = (family: string, options = "") => + `https://fonts.googleapis.com/css2?family=${encodeURIComponent(family)}${options}&display=swap`; + +const loadFontStyle = (url: string) => { + document.head.insertAdjacentHTML("beforeend", ``); + return document.createElement("style"); +}; + async function searchGoogleFonts(query: string) { try { const response = await fetch("https://fonts.google.com/$rpc/fonts.fe.catalog.actions.metadata.MetadataService/FontSearch", { @@ -36,14 +45,12 @@ async function searchGoogleFonts(query: string) { "content-type": "application/json+protobuf", "x-user-agent": "grpc-web-javascript/0.1" }, - // the nulls are optional filters body: JSON.stringify([[query, null, null, null, null, null, 1], [5], null, 16]) }); const data = await response.json(); if (!data?.[1]) return []; - // god please help me - const fonts = data[1].map(([_, fontData]: [string, any[]]) => ({ + return data[1].map(([_, fontData]: [string, any[]]) => ({ family: fontData[0], displayName: fontData[1], authors: fontData[2], @@ -54,81 +61,52 @@ async function searchGoogleFonts(query: string) { })) })) })); - return fonts; - // LETS GO IT FUCKING WORKSSSSSSSSSSSS } catch (err) { console.error("Failed to fetch fonts:", err); return []; } } -async function preloadFont(family: string) { - // https://developers.google.com/fonts/docs/css2 - const url = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(family)}&text=The quick brown fox jumps over the lazy dog&display=swap`; - const css = await fetch(url, { - headers: { - "User-Agent": userAgent - } - }).then(r => r.text()); +const preloadFont = (family: string) => + loadFontStyle(createGoogleFontUrl(family, "&text=The quick brown fox jumps over the lazy dog")); - const style = document.createElement("style"); - style.textContent = css; - document.head.appendChild(style); - return style; -} +let styleElement: HTMLStyleElement | null = null; -async function applyFont(fontFamily: string) { +const applyFont = async (fontFamily: string) => { if (!fontFamily) { - if (styleElement) { - styleElement.remove(); - styleElement = null; - } + styleElement?.remove(); + styleElement = null; return; } try { - const response = await fetch( - `https://fonts.googleapis.com/css2?family=${encodeURIComponent(fontFamily)}:wght@300;400;500;600;700&display=swap`, - { - headers: { - "User-Agent": userAgent - } - } - ); - const css = await response.text(); - if (!styleElement) { styleElement = document.createElement("style"); document.head.appendChild(styleElement); } + loadFontStyle(createGoogleFontUrl(fontFamily, ":wght@300;400;500;600;700")); styleElement.textContent = ` - ${css} - * { - --font-primary: '${fontFamily}', sans-serif !important; - --font-display: '${fontFamily}', sans-serif !important; - --font-headline: '${fontFamily}', sans-serif !important; - --font-code: '${fontFamily}', monospace !important; - } - `; + * { + --font-primary: '${fontFamily}', sans-serif !important; + --font-display: '${fontFamily}', sans-serif !important; + --font-headline: '${fontFamily}', sans-serif !important; + --font-code: '${fontFamily}', monospace !important; + } + `; } catch (err) { console.error("Failed to load font:", err); } -} +}; function GoogleFontSearch({ onSelect }: { onSelect: (font: GoogleFontMetadata) => void; }) { const [query, setQuery] = React.useState(""); const [results, setResults] = React.useState([]); const [loading, setLoading] = React.useState(false); - - const previewStyles = React.useRef([]); - - React.useEffect(() => { - return () => { - previewStyles.current.forEach(style => style.remove()); - }; + React.useEffect(() => () => { + previewStyles.current.forEach(style => style.remove()); }, []); const debouncedSearch = debounce(async (value: string) => { @@ -138,22 +116,18 @@ function GoogleFontSearch({ onSelect }: { onSelect: (font: GoogleFontMetadata) = setLoading(false); return; } + const fonts = await searchGoogleFonts(value); - previewStyles.current.forEach(style => style.remove()); - previewStyles.current = []; - - const styles = await Promise.all(fonts.map(f => preloadFont(f.family))); - previewStyles.current = styles; - + previewStyles.current = await Promise.all(fonts.map(f => preloadFont(f.family))); setResults(fonts); setLoading(false); }, 300); - const handleSearch = React.useCallback((value: string) => { - setQuery(value); - debouncedSearch(value); - }, []); + const handleSearch = (e: string) => { + setQuery(e); + debouncedSearch(e); + }; return ( @@ -193,8 +167,6 @@ function GoogleFontSearch({ onSelect }: { onSelect: (font: GoogleFontMetadata) = ); } -let styleElement: HTMLStyleElement | null = null; - const settings = definePluginSettings({ selectedFont: { type: OptionType.STRING, From 6b4b853fc7f1708c34ae5697f80bd8f254fe892a Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 12 Feb 2025 15:48:03 -0500 Subject: [PATCH 05/34] Update common.mjs --- scripts/build/common.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index b71c1f60..17fc2cd3 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -364,7 +364,6 @@ const escapedBuiltinModules = builtinModules const builtinModuleRegex = new RegExp(`^(node:)?(${escapedBuiltinModules})$`); export const commonRendererPlugins = [ - banImportPlugin(builtinModuleRegex, "Cannot import node inbuilt modules in browser code. You need to use a native.ts file"), banImportPlugin(/^react$/, "Cannot import from react. React and hooks should be imported from @webpack/common"), banImportPlugin(/^electron(\/.*)?$/, "Cannot import electron in browser code. You need to use a native.ts file"), banImportPlugin(/^ts-pattern$/, "Cannot import from ts-pattern. match and P should be imported from @webpack/common"), From 03d55517f26b0091f0f2e94b73b146fb14e45d48 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 12 Feb 2025 15:50:09 -0500 Subject: [PATCH 06/34] Fix Compiling --- package.json | 1 + pnpm-lock.yaml | 8 ++++++++ scripts/build/common.mjs | 1 - 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index afa0cf72..854b96b4 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@ffmpeg/ffmpeg": "^0.12.10", "@ffmpeg/util": "^0.12.1", "@intrnl/xxhash64": "^0.1.2", + "@sapphi-red/web-noise-suppressor": "0.3.5", "@types/less": "^3.0.6", "@types/stylus": "^0.48.42", "@types/tinycolor2": "^1.4.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 18887552..6a71ee39 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,6 +25,9 @@ importers: '@intrnl/xxhash64': specifier: ^0.1.2 version: 0.1.2 + '@sapphi-red/web-noise-suppressor': + specifier: 0.3.5 + version: 0.3.5 '@types/less': specifier: ^3.0.6 version: 3.0.6(patch_hash=krcufrsfhsuxuoj7hocqugs6zi) @@ -802,6 +805,9 @@ packages: engines: {node: '>=18'} hasBin: true + '@sapphi-red/web-noise-suppressor@0.3.5': + resolution: {integrity: sha512-jh3+V9yM+zxLriQexoGm0GatoPaJWjs6ypFIbFYwQp+AoUb55eUXrjKtKQyuC5zShzzeAQUl0M5JzqB7SSrsRA==} + '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} @@ -3349,6 +3355,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@sapphi-red/web-noise-suppressor@0.3.5': {} + '@socket.io/component-emitter@3.1.2': {} '@stylistic/eslint-plugin@2.12.1(eslint@9.17.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4))(typescript@5.7.2)': diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index 17fc2cd3..cfa46865 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -361,7 +361,6 @@ export const commonOpts = { const escapedBuiltinModules = builtinModules .map(m => m.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")) .join("|"); -const builtinModuleRegex = new RegExp(`^(node:)?(${escapedBuiltinModules})$`); export const commonRendererPlugins = [ banImportPlugin(/^react$/, "Cannot import from react. React and hooks should be imported from @webpack/common"), From cbdafae3f4fb4be502bba51dd34e1118c706880a Mon Sep 17 00:00:00 2001 From: Cassie <37855219+CodeF53@users.noreply.github.com> Date: Wed, 12 Feb 2025 15:24:33 -0700 Subject: [PATCH 07/34] fix for SettingsSync DataStore (#149) --- src/utils/settingsSync.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/settingsSync.ts b/src/utils/settingsSync.ts index a31aa621..c4490130 100644 --- a/src/utils/settingsSync.ts +++ b/src/utils/settingsSync.ts @@ -35,11 +35,11 @@ export async function importSettings(data: string) { throw new Error("Failed to parse JSON: " + String(err)); } - if ("settings" in parsed && "quickCss" in parsed && "dataStore" in parsed) { + if ("settings" in parsed && "quickCss" in parsed) { Object.assign(PlainSettings, parsed.settings); await VencordNative.settings.set(parsed.settings); await VencordNative.quickCss.set(parsed.quickCss); - await DataStore.setMany(parsed.dataStore); + if (parsed.dataStore) await DataStore.setMany(parsed.dataStore); } else throw new Error("Invalid Settings. Is this even an Equicord Settings file?"); } From fad2f1e5b4016f095029f85bcbe25b2385a6f445 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 12 Feb 2025 19:26:47 -0300 Subject: [PATCH 08/34] Fix PatchHelper and DevCompanion not replacing $self correctly --- src/components/VencordSettings/PatchHelperTab.tsx | 2 +- src/plugins/devCompanion.dev/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/VencordSettings/PatchHelperTab.tsx b/src/components/VencordSettings/PatchHelperTab.tsx index f3a8e1dd..2bb4047f 100644 --- a/src/components/VencordSettings/PatchHelperTab.tsx +++ b/src/components/VencordSettings/PatchHelperTab.tsx @@ -65,7 +65,7 @@ function ReplacementComponent({ module, match, replacement, setReplacementError } const canonicalMatch = canonicalizeMatch(new RegExp(match)); try { - const canonicalReplace = canonicalizeReplace(replacement, "YourPlugin"); + const canonicalReplace = canonicalizeReplace(replacement, 'Vencord.Plugins.plugins["YourPlugin"]"'); var patched = src.replace(canonicalMatch, canonicalReplace as string); setReplacementError(void 0); } catch (e) { diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index d6a56fe6..d780b592 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -173,7 +173,7 @@ function initWs(isManual = false) { try { const matcher = canonicalizeMatch(parseNode(match)); - const replacement = canonicalizeReplace(parseNode(replace), "PlaceHolderPluginName"); + const replacement = canonicalizeReplace(parseNode(replace), 'Vencord.Plugins.plugins["PlaceHolderPluginName"]"'); const newSource = src.replace(matcher, replacement as string); From c29088d920f35af01d46d6595a4c29006e7dc4b9 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 12 Feb 2025 20:35:31 -0500 Subject: [PATCH 09/34] Fix Reporter & Web --- scripts/build/buildWeb.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/buildWeb.mjs b/scripts/build/buildWeb.mjs index 1bd0964a..da1e221a 100644 --- a/scripts/build/buildWeb.mjs +++ b/scripts/build/buildWeb.mjs @@ -90,7 +90,7 @@ const buildConfigs = [ }, { ...commonOptions, - outfile: "dist/browser.js", + outfile: "dist/browser/browser.js", footer: { js: "//# sourceURL=VencordWeb" } }, { From ebb65b73ad0ec6994351e04a36c5656601836da9 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 12 Feb 2025 20:38:37 -0500 Subject: [PATCH 10/34] Fix for a fix --- scripts/build/buildWeb.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/build/buildWeb.mjs b/scripts/build/buildWeb.mjs index da1e221a..9b27f2e7 100644 --- a/scripts/build/buildWeb.mjs +++ b/scripts/build/buildWeb.mjs @@ -49,6 +49,7 @@ const commonOptions = { IS_COMPANION_TEST, IS_DISCORD_DESKTOP: false, IS_VESKTOP: false, + IS_EQUIBOP: false, IS_UPDATER_DISABLED: true, VERSION, BUILD_TIMESTAMP From f9cc400757a248f005913ffb5178b49772ca25ef Mon Sep 17 00:00:00 2001 From: xou09 <156519349+xou09@users.noreply.github.com> Date: Thu, 13 Feb 2025 19:05:09 +0300 Subject: [PATCH 11/34] FriendInvites: fix not working in dms (#3226) --- src/plugins/friendInvites/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/friendInvites/index.ts b/src/plugins/friendInvites/index.ts index 3c5a324f..0af611bc 100644 --- a/src/plugins/friendInvites/index.ts +++ b/src/plugins/friendInvites/index.ts @@ -31,7 +31,7 @@ export default definePlugin({ { name: "create friend invite", description: "Generates a friend invite link.", - inputType: ApplicationCommandInputType.BOT, + inputType: ApplicationCommandInputType.BUILT_IN, execute: async (args, ctx) => { const invite = await FriendInvites.createFriendInvite(); @@ -48,7 +48,7 @@ export default definePlugin({ { name: "view friend invites", description: "View a list of all generated friend invites.", - inputType: ApplicationCommandInputType.BOT, + inputType: ApplicationCommandInputType.BUILT_IN, execute: async (_, ctx) => { const invites = await FriendInvites.getAllFriendInvites(); const friendInviteList = invites.map(i => @@ -67,7 +67,7 @@ export default definePlugin({ { name: "revoke friend invites", description: "Revokes all generated friend invites.", - inputType: ApplicationCommandInputType.BOT, + inputType: ApplicationCommandInputType.BUILT_IN, execute: async (_, ctx) => { await FriendInvites.revokeFriendInvites(); From cdfbe33052ff392c0dfbb71107a6ad68e776c922 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 13 Feb 2025 13:31:05 -0500 Subject: [PATCH 12/34] Fix StatusPresets --- src/equicordplugins/statusPresets/index.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/equicordplugins/statusPresets/index.tsx b/src/equicordplugins/statusPresets/index.tsx index 904f30c3..17684d5c 100644 --- a/src/equicordplugins/statusPresets/index.tsx +++ b/src/equicordplugins/statusPresets/index.tsx @@ -24,12 +24,11 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { EquicordDevs } from "@utils/constants"; import { proxyLazy } from "@utils/lazy"; import { classes } from "@utils/misc"; -import { ModalProps, openModalLazy } from "@utils/modal"; +import { openModalLazy } from "@utils/modal"; import { useForceUpdater } from "@utils/react"; import definePlugin, { OptionType, StartAt } from "@utils/types"; import { extractAndLoadChunksLazy, findByPropsLazy, findComponentByCodeLazy, findModuleId, wreq } from "@webpack"; import { Button, Clickable, Menu, Toasts, UserStore, useState } from "@webpack/common"; -import { FunctionComponent } from "react"; const settings = definePluginSettings({ @@ -62,7 +61,7 @@ const PMenu = findComponentByCodeLazy(".menuItemLabel", ".menuItemInner"); const EmojiComponent = findComponentByCodeLazy(/\.translateSurrogatesToInlineEmoji\(\i.\i\),/); const CustomStatusSettings = getUserSettingLazy("status", "customStatus")!; -const StatsModule: { default: FunctionComponent; } = proxyLazy(() => { +const StatusModule = proxyLazy(() => { const id = findModuleId("this.renderCustomStatusInput()"); return wreq(Number(id)); }); @@ -71,7 +70,9 @@ const requireCustomStatusModal = extractAndLoadChunksLazy(["action:\"PRESS_ADD_C const openCustomStatusModalLazy = () => openModalLazy(async () => { await requireCustomStatusModal(); - return props => ; + const key = Object.keys(StatusModule)[0]; + const Component = StatusModule[key]; + return props => ; }); function getExpirationMs(expiration: "TODAY" | number) { From f385879b4bbdfbe99ac7b72c986243aa127c1c52 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 14 Feb 2025 02:26:41 -0500 Subject: [PATCH 13/34] Fix plugin imports --- src/components/ThemeSettings/ThemesTab.tsx | 2 +- tsconfig.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/ThemeSettings/ThemesTab.tsx b/src/components/ThemeSettings/ThemesTab.tsx index 18996136..12e49d6d 100644 --- a/src/components/ThemeSettings/ThemesTab.tsx +++ b/src/components/ThemeSettings/ThemesTab.tsx @@ -27,7 +27,7 @@ import { openPluginModal } from "@components/PluginSettings/PluginModal"; import { AddonCard } from "@components/VencordSettings/AddonCard"; import { QuickAction, QuickActionCard } from "@components/VencordSettings/quickActions"; import { SettingsTab, wrapTab } from "@components/VencordSettings/shared"; -import { isPluginEnabled } from "@plugins"; +import { isPluginEnabled } from "@plugins/index"; import { openInviteModal } from "@utils/discord"; import { openModal } from "@utils/modal"; import { showItemInFolder } from "@utils/native"; diff --git a/tsconfig.json b/tsconfig.json index d5d6f942..c4c620c6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -31,8 +31,8 @@ "@webpack": ["./webpack/webpack"], "@webpack/patcher": ["./webpack/patchWebpack"], "@webpack/wreq.d": ["./webpack/wreq.d"], - "@plugins": ["./plugins"], - "@equicordplugins": ["./equicordplugins"] + "@plugins/*": ["./plugins/*"], + "@equicordplugins/*": ["./equicordplugins/*"] }, "plugins": [ // Transform paths in output .d.ts files (Include this line if you output declarations files) From 7ca3bbb8e6fb084fe132e569806bbfcd360f1a49 Mon Sep 17 00:00:00 2001 From: mochie Date: Fri, 14 Feb 2025 08:27:38 +0100 Subject: [PATCH 14/34] Add customUserColors (#150) * add preferFriends to showMeYourName * Add customUserColors * pressing enter closes modal * please update your discord types package * sowwy.. * Do A Settings Check For Typing Tweaks * Do a proper import on rolecoloreverywhere --------- Co-authored-by: thororen <78185467+thororen1234@users.noreply.github.com> --- .../customUserColors/SetColorModal.tsx | 95 ++++++++++++++ .../customUserColors/index.tsx | 119 ++++++++++++++++++ .../customUserColors/styles.css | 16 +++ src/plugins/roleColorEverywhere/index.tsx | 6 +- src/plugins/typingTweaks/index.tsx | 11 +- 5 files changed, 244 insertions(+), 3 deletions(-) create mode 100644 src/equicordplugins/customUserColors/SetColorModal.tsx create mode 100644 src/equicordplugins/customUserColors/index.tsx create mode 100644 src/equicordplugins/customUserColors/styles.css diff --git a/src/equicordplugins/customUserColors/SetColorModal.tsx b/src/equicordplugins/customUserColors/SetColorModal.tsx new file mode 100644 index 00000000..ba8919f9 --- /dev/null +++ b/src/equicordplugins/customUserColors/SetColorModal.tsx @@ -0,0 +1,95 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { set } from "@api/DataStore"; +import { classNameFactory } from "@api/Styles"; +import { Margins } from "@utils/margins"; +import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal"; +import { findComponentByCodeLazy } from "@webpack"; +import { Button, Forms, useState } from "@webpack/common"; + +import { colors, DATASTORE_KEY } from "./index"; + +interface ColorPickerProps { + color: number; + showEyeDropper?: boolean; + suggestedColors?: string[]; + onChange(value: number | null): void; +} +const ColorPicker = findComponentByCodeLazy("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", ".BACKGROUND_PRIMARY)"); + +const cl = classNameFactory("vc-customColors-"); + +export function SetColorModal({ userId, modalProps }: { userId: string, modalProps: ModalProps; }) { + + const userColor = colors[userId]; + + const initialColor = parseInt(colors[userId], 16) || 372735; + // color picker default to current color set for user (if null it's 0x05afff :3 ) + + const [colorPickerColor, setColorPickerColor] = useState(initialColor); + // hex color code as an int (NOT rgb 0-255) + + + function setUserColor(color: number) { + setColorPickerColor(color); + } + + function handleKey(e: KeyboardEvent) { + if (e.key === "Enter") + saveUserColor(); + } + + async function saveUserColor() { + colors[userId] = colorPickerColor.toString(16).padStart(6, "0"); + await set(DATASTORE_KEY, colors); + modalProps.onClose(); + } + + async function deleteUserColor() { + delete colors[userId]; + await set(DATASTORE_KEY, colors); + modalProps.onClose(); + } + + return ( + + + + Custom Color + + + + +
+ + Pick a color + + +
+
+ + + + + +
+ ); +} diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx new file mode 100644 index 00000000..b204350d --- /dev/null +++ b/src/equicordplugins/customUserColors/index.tsx @@ -0,0 +1,119 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./styles.css"; + +import { NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { get } from "@api/DataStore"; +import { definePluginSettings, Settings } from "@api/Settings"; +import { EquicordDevs } from "@utils/constants"; +import { openModal } from "@utils/modal"; +import definePlugin, { OptionType } from "@utils/types"; +import { extractAndLoadChunksLazy } from "@webpack"; +import { Menu, UserStore } from "@webpack/common"; +import { User } from "discord-types/general"; + +import { SetColorModal } from "./SetColorModal"; + +export const DATASTORE_KEY = "equicord-customcolors"; +export let colors: Record = {}; +(async () => { + colors = await get>(DATASTORE_KEY) || {}; +})(); + +const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}(\i\.\i\("?.+?"?\).*?).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}"UserSettings"/); +// needed for color picker to be available without opening settings (ty pindms!!) +const userContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: { user: User; }) => { + if (user?.id == null) return; + + const setCustomColorItem = ( + { + await requireSettingsMenu(); + openModal(modalProps => ); + }} + /> + ); + + children.push(, setCustomColorItem); + +}; + +export function getCustomColorString(userId: string, withHash?: boolean): string | undefined { + if (!colors[userId] || !Settings.plugins.customUserColors.enabled) + return; + + if (withHash) + return `#${colors[userId]}`; + + return colors[userId]; +} + +const settings = definePluginSettings({ + DmList: { + type: OptionType.BOOLEAN, + description: "Users with custom colors defined will have their name in the dm list colored", + default: true, + }, + colorInServers: { + type: OptionType.BOOLEAN, + description: "If name colors should be changed within servers", + default: true, + } +}); + +export default definePlugin({ + name: "customUserColors", + description: "Lets you add a custom color to any user, anywhere! Highly recommend to use with typingTweaks and roleColorEverywhere", + authors: [EquicordDevs.mochienya], + contextMenus: { "user-context": userContextMenuPatch }, + settings, + requireSettingsMenu, + getCustomColorString, + + patches: [ + { + // this also affects name headers in chats outside of servers + find: /type:\i\.\i\.Types\.REMIX/, + replacement: { + match: /style:"username".*?void 0/, + replace: "style:{color:$self.colorIfServer(arguments[0])}" + } + }, + { + predicate: () => settings.store.DmList, + find: /muted:\i=!1,highlighted:\i=!1/, + replacement: { + match: /(nameAndDecorators,)/, + replace: "$1style:{color:$self.colorDMList(arguments[0])}," + }, + }, + ], + + colorDMList(a: any): string | undefined { + try { + // @ts-ignore + const { id } = UserStore.findByTag(a.avatar.props["aria-label"]); + // get user id by props on avatars having username as aria label + const colorString = getCustomColorString(id, true); + if (colorString) + return colorString; + return "inherit"; + } catch { return; } // if you have a group in your dms then discord will crash on load without this + }, + + colorIfServer(a: any): string | undefined { + const roleColor = a.author.colorString; + + if (a.channel.guild_id && !settings.store.colorInServers) + return roleColor; + + const color = getCustomColorString(a.message.author.id, true); + return color ?? roleColor; + } +}); diff --git a/src/equicordplugins/customUserColors/styles.css b/src/equicordplugins/customUserColors/styles.css new file mode 100644 index 00000000..d48407d3 --- /dev/null +++ b/src/equicordplugins/customUserColors/styles.css @@ -0,0 +1,16 @@ +.vc-customColors-modal-header { + place-content: center; + justify-content: space-between; +} + +.vc-customColors-modal-header h1 { + margin: 0; +} + +.vc-customColors-modal-content { + padding: 1em; +} + +.vc-customColors-modal-footer { + gap: 16px; +} diff --git a/src/plugins/roleColorEverywhere/index.tsx b/src/plugins/roleColorEverywhere/index.tsx index 7b7bcc44..0330193c 100644 --- a/src/plugins/roleColorEverywhere/index.tsx +++ b/src/plugins/roleColorEverywhere/index.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { definePluginSettings } from "@api/Settings"; +import { definePluginSettings, Settings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { makeRange } from "@components/PluginSettings/components"; import { Devs } from "@utils/constants"; @@ -24,6 +24,7 @@ import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; import { findByCodeLazy } from "@webpack"; import { ChannelStore, GuildMemberStore, GuildStore } from "@webpack/common"; +import { getCustomColorString } from "@equicordplugins/customUserColors"; const useMessageAuthor = findByCodeLazy('"Result cannot be null because the message is not null"'); @@ -164,6 +165,9 @@ export default definePlugin({ getColorString(userId: string, channelOrGuildId: string) { try { + if (Settings.plugins.customUserColors.enabled) + return getCustomColorString(userId, true); + const guildId = ChannelStore.getChannel(channelOrGuildId)?.guild_id ?? GuildStore.getGuild(channelOrGuildId)?.id; if (guildId == null) return null; diff --git a/src/plugins/typingTweaks/index.tsx b/src/plugins/typingTweaks/index.tsx index ff68a486..ec86f0aa 100644 --- a/src/plugins/typingTweaks/index.tsx +++ b/src/plugins/typingTweaks/index.tsx @@ -16,13 +16,14 @@ * along with this program. If not, see . */ -import { definePluginSettings } from "@api/Settings"; +import { definePluginSettings, Settings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { openUserProfile } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; import { Avatar, GuildMemberStore, React, RelationshipStore } from "@webpack/common"; import { User } from "discord-types/general"; +import { getCustomColorString } from "@equicordplugins/customUserColors"; import { PropsWithChildren } from "react"; const settings = definePluginSettings({ @@ -57,6 +58,12 @@ interface Props { guildId: string; } +function TypingUserColor(guildId: string, userId: string) { + if (!settings.store.showRoleColors) return; + if (Settings.plugins.customUserColors.enabled) return getCustomColorString(userId, true); + return GuildMemberStore.getMember(guildId, userId)?.colorString; +} + const TypingUser = ErrorBoundary.wrap(function ({ user, guildId }: Props) { return ( From d7d9e7dca16965069045c3c4999d8607c69258b4 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 14 Feb 2025 02:46:00 -0500 Subject: [PATCH 15/34] CustomUserColors: Remove Ignore --- .../customUserColors/SetColorModal.tsx | 65 +++++++++---------- .../customUserColors/index.tsx | 20 +++--- .../fixFileExtensions/components.tsx | 19 ------ .../fixFileExtensions/index.tsx | 14 +++- src/equicordplugins/glide/index.tsx | 2 +- .../themeLibrary/components/ThemeTab.tsx | 2 +- src/plugins/anonymiseFileNames/index.tsx | 3 +- src/plugins/roleColorEverywhere/index.tsx | 2 +- src/plugins/typingTweaks/index.tsx | 4 +- 9 files changed, 59 insertions(+), 72 deletions(-) delete mode 100644 src/equicordplugins/fixFileExtensions/components.tsx diff --git a/src/equicordplugins/customUserColors/SetColorModal.tsx b/src/equicordplugins/customUserColors/SetColorModal.tsx index ba8919f9..bdb36d6a 100644 --- a/src/equicordplugins/customUserColors/SetColorModal.tsx +++ b/src/equicordplugins/customUserColors/SetColorModal.tsx @@ -24,9 +24,6 @@ const ColorPicker = findComponentByCodeLazy("#{intl::USER_SETT const cl = classNameFactory("vc-customColors-"); export function SetColorModal({ userId, modalProps }: { userId: string, modalProps: ModalProps; }) { - - const userColor = colors[userId]; - const initialColor = parseInt(colors[userId], 16) || 372735; // color picker default to current color set for user (if null it's 0x05afff :3 ) @@ -57,39 +54,39 @@ export function SetColorModal({ userId, modalProps }: { userId: string, modalPro return ( - - - Custom Color + + + Custom Color + + + + +
+ + Pick a color - - - -
- - Pick a color - - -
-
+ +
+
- - - - + + + +
); } diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx index b204350d..e99d1e5a 100644 --- a/src/equicordplugins/customUserColors/index.tsx +++ b/src/equicordplugins/customUserColors/index.tsx @@ -13,19 +13,21 @@ import { EquicordDevs } from "@utils/constants"; import { openModal } from "@utils/modal"; import definePlugin, { OptionType } from "@utils/types"; import { extractAndLoadChunksLazy } from "@webpack"; -import { Menu, UserStore } from "@webpack/common"; +import { Menu } from "@webpack/common"; import { User } from "discord-types/general"; import { SetColorModal } from "./SetColorModal"; export const DATASTORE_KEY = "equicord-customcolors"; export let colors: Record = {}; + (async () => { colors = await get>(DATASTORE_KEY) || {}; })(); -const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}(\i\.\i\("?.+?"?\).*?).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}"UserSettings"/); // needed for color picker to be available without opening settings (ty pindms!!) +const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}(\i\.\i\("?.+?"?\).*?).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}"UserSettings"/); + const userContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: { user: User; }) => { if (user?.id == null) return; @@ -96,15 +98,11 @@ export default definePlugin({ ], colorDMList(a: any): string | undefined { - try { - // @ts-ignore - const { id } = UserStore.findByTag(a.avatar.props["aria-label"]); - // get user id by props on avatars having username as aria label - const colorString = getCustomColorString(id, true); - if (colorString) - return colorString; - return "inherit"; - } catch { return; } // if you have a group in your dms then discord will crash on load without this + const userId = a?.subText?.props?.user?.id; + if (!userId) return; + const colorString = getCustomColorString(userId, true); + if (colorString) return colorString; + return "inherit"; }, colorIfServer(a: any): string | undefined { diff --git a/src/equicordplugins/fixFileExtensions/components.tsx b/src/equicordplugins/fixFileExtensions/components.tsx deleted file mode 100644 index 3c9322f2..00000000 --- a/src/equicordplugins/fixFileExtensions/components.tsx +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -const extensionMap = { - "ogg": [".ogv", ".oga", ".ogx", ".ogm", ".spx", ".opus"], - "jpg": [".jpg", ".jpeg", ".jfif", ".jpe", ".jif", ".jfi", ".pjpeg", ".pjp"], - "svg": [".svgz"], - "mp4": [".m4v", ".m4r", ".m4p"], - "m4a": [".m4b"], - "mov": [".movie", ".qt"], -}; - -export const reverseExtensionMap = Object.entries(extensionMap).reduce((acc, [target, exts]) => { - exts.forEach(ext => acc[ext] = `.${target}`); - return acc; -}, {} as Record); diff --git a/src/equicordplugins/fixFileExtensions/index.tsx b/src/equicordplugins/fixFileExtensions/index.tsx index 7e1b646a..18df49d4 100644 --- a/src/equicordplugins/fixFileExtensions/index.tsx +++ b/src/equicordplugins/fixFileExtensions/index.tsx @@ -9,7 +9,19 @@ import { Settings } from "@api/Settings"; import { EquicordDevs } from "@utils/constants"; import definePlugin, { ReporterTestable } from "@utils/types"; -import { reverseExtensionMap } from "./components"; +const extensionMap = { + "ogg": [".ogv", ".oga", ".ogx", ".ogm", ".spx", ".opus"], + "jpg": [".jpg", ".jpeg", ".jfif", ".jpe", ".jif", ".jfi", ".pjpeg", ".pjp"], + "svg": [".svgz"], + "mp4": [".m4v", ".m4r", ".m4p"], + "m4a": [".m4b"], + "mov": [".movie", ".qt"], +}; + +export const reverseExtensionMap = Object.entries(extensionMap).reduce((acc, [target, exts]) => { + exts.forEach(ext => acc[ext] = `.${target}`); + return acc; +}, {} as Record); type ExtUpload = Upload & { fixExtension?: boolean; }; diff --git a/src/equicordplugins/glide/index.tsx b/src/equicordplugins/glide/index.tsx index f195800b..5547133a 100644 --- a/src/equicordplugins/glide/index.tsx +++ b/src/equicordplugins/glide/index.tsx @@ -797,5 +797,5 @@ export default definePlugin({ }, startAt: StartAt.DOMContentLoaded, // preview thing, kinda low effort but eh - settingsAboutComponent: () => + settingsAboutComponent: () => }); diff --git a/src/equicordplugins/themeLibrary/components/ThemeTab.tsx b/src/equicordplugins/themeLibrary/components/ThemeTab.tsx index 5d8c95a9..4a98b920 100644 --- a/src/equicordplugins/themeLibrary/components/ThemeTab.tsx +++ b/src/equicordplugins/themeLibrary/components/ThemeTab.tsx @@ -275,7 +275,7 @@ function SubmitThemes() { color: "var(--text-normal)" }}>

This tab was replaced in favour of the new website:

-

discord-themes.com

+

discord-themes.com

Date: Fri, 14 Feb 2025 21:57:23 -0300 Subject: [PATCH 16/34] Remove trailing quote from testing patches --- src/components/VencordSettings/PatchHelperTab.tsx | 2 +- src/plugins/devCompanion.dev/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/VencordSettings/PatchHelperTab.tsx b/src/components/VencordSettings/PatchHelperTab.tsx index 2bb4047f..f930a40d 100644 --- a/src/components/VencordSettings/PatchHelperTab.tsx +++ b/src/components/VencordSettings/PatchHelperTab.tsx @@ -65,7 +65,7 @@ function ReplacementComponent({ module, match, replacement, setReplacementError } const canonicalMatch = canonicalizeMatch(new RegExp(match)); try { - const canonicalReplace = canonicalizeReplace(replacement, 'Vencord.Plugins.plugins["YourPlugin"]"'); + const canonicalReplace = canonicalizeReplace(replacement, 'Vencord.Plugins.plugins["YourPlugin"]'); var patched = src.replace(canonicalMatch, canonicalReplace as string); setReplacementError(void 0); } catch (e) { diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index d780b592..4ac2e993 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -173,7 +173,7 @@ function initWs(isManual = false) { try { const matcher = canonicalizeMatch(parseNode(match)); - const replacement = canonicalizeReplace(parseNode(replace), 'Vencord.Plugins.plugins["PlaceHolderPluginName"]"'); + const replacement = canonicalizeReplace(parseNode(replace), 'Vencord.Plugins.plugins["PlaceHolderPluginName"]'); const newSource = src.replace(matcher, replacement as string); From d77fd04a6416fd07f12d248a2a8658bbdae59209 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 14 Feb 2025 21:22:01 -0500 Subject: [PATCH 17/34] Fixes For Canary --- src/equicordplugins/betterInvites/index.tsx | 5 +++-- src/equicordplugins/customUserColors/index.tsx | 12 +++++++----- src/equicordplugins/whosWatching/index.tsx | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/equicordplugins/betterInvites/index.tsx b/src/equicordplugins/betterInvites/index.tsx index a2c650ba..d72cbb74 100644 --- a/src/equicordplugins/betterInvites/index.tsx +++ b/src/equicordplugins/betterInvites/index.tsx @@ -44,8 +44,8 @@ export default definePlugin({ replace: ",($1||((!$1)&&arguments[0].invite.expires_at)) && $2$self.RenderTip($1, $3, arguments[0].invite.expires_at)" }, { - match: /(\.jsx\)\(\i.\i.Info,{.+onClick):(\i\?\i:null),/, - replace: "$1:$2 || $self.Lurkable(arguments[0].invite.guild.id, arguments[0].invite.guild.features)," + match: /(\.jsx\)\(\i.\i.Info,{.+onClick:\i\?.{0,5}:null)/, + replace: "$& || $self.Lurkable(arguments[0].invite.guild.id, arguments[0].invite.guild.features)" }, { match: /(\.jsx\)\(\i\.\i\.Header,\{)text:(\i)/, @@ -61,6 +61,7 @@ export default definePlugin({ return

{(inviter && (currentUserId !== inviter.id)) ? <> openUserProfile(inviter.id)} src={inviter.avatar ? `https://cdn.discordapp.com/avatars/${inviter.id}/${inviter.avatar}.webp?size=80` : "/assets/1f0bfc0865d324c2587920a7d80c609b.png?size=128"} diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx index e99d1e5a..24b51d12 100644 --- a/src/equicordplugins/customUserColors/index.tsx +++ b/src/equicordplugins/customUserColors/index.tsx @@ -8,7 +8,7 @@ import "./styles.css"; import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { get } from "@api/DataStore"; -import { definePluginSettings, Settings } from "@api/Settings"; +import { definePluginSettings, migratePluginSettings, Settings } from "@api/Settings"; import { EquicordDevs } from "@utils/constants"; import { openModal } from "@utils/modal"; import definePlugin, { OptionType } from "@utils/types"; @@ -57,7 +57,7 @@ export function getCustomColorString(userId: string, withHash?: boolean): string } const settings = definePluginSettings({ - DmList: { + dmList: { type: OptionType.BOOLEAN, description: "Users with custom colors defined will have their name in the dm list colored", default: true, @@ -69,8 +69,10 @@ const settings = definePluginSettings({ } }); + +migratePluginSettings("CustomUserColors", "customUserColors"); export default definePlugin({ - name: "customUserColors", + name: "CustomUserColors", description: "Lets you add a custom color to any user, anywhere! Highly recommend to use with typingTweaks and roleColorEverywhere", authors: [EquicordDevs.mochienya], contextMenus: { "user-context": userContextMenuPatch }, @@ -83,12 +85,12 @@ export default definePlugin({ // this also affects name headers in chats outside of servers find: /type:\i\.\i\.Types\.REMIX/, replacement: { - match: /style:"username".*?void 0/, + match: /style:"username".{0,50}void 0/, replace: "style:{color:$self.colorIfServer(arguments[0])}" } }, { - predicate: () => settings.store.DmList, + predicate: () => settings.store.dmList, find: /muted:\i=!1,highlighted:\i=!1/, replacement: { match: /(nameAndDecorators,)/, diff --git a/src/equicordplugins/whosWatching/index.tsx b/src/equicordplugins/whosWatching/index.tsx index 6755c66e..33a13a11 100644 --- a/src/equicordplugins/whosWatching/index.tsx +++ b/src/equicordplugins/whosWatching/index.tsx @@ -51,7 +51,7 @@ function Watching({ userIds, guildId }: WatchingProps): JSX.Element { {users.map(user => ( - + {getUsername(user)} ))} From 0ba61b1c7ab4fc261d21fd5c76da47bee4d7aa4b Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 14 Feb 2025 21:29:40 -0500 Subject: [PATCH 18/34] Fix Crashing --- src/equicordplugins/customUserColors/index.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx index 24b51d12..bf64d5ce 100644 --- a/src/equicordplugins/customUserColors/index.tsx +++ b/src/equicordplugins/customUserColors/index.tsx @@ -47,7 +47,7 @@ const userContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: { }; export function getCustomColorString(userId: string, withHash?: boolean): string | undefined { - if (!colors[userId] || !Settings.plugins.customUserColors.enabled) + if (!colors[userId] || !Settings.plugins.CustomUserColors.enabled) return; if (withHash) @@ -110,8 +110,7 @@ export default definePlugin({ colorIfServer(a: any): string | undefined { const roleColor = a.author.colorString; - if (a.channel.guild_id && !settings.store.colorInServers) - return roleColor; + if (a.channel.guild_id && !settings.store.colorInServers) return roleColor; const color = getCustomColorString(a.message.author.id, true); return color ?? roleColor; From 5df681a1210a3121821c8d77bbeeed7f24be0006 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 14 Feb 2025 21:35:56 -0500 Subject: [PATCH 19/34] Fixes for CustomUserColors --- src/equicordplugins/customUserColors/index.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx index bf64d5ce..d9ff6a71 100644 --- a/src/equicordplugins/customUserColors/index.tsx +++ b/src/equicordplugins/customUserColors/index.tsx @@ -85,8 +85,8 @@ export default definePlugin({ // this also affects name headers in chats outside of servers find: /type:\i\.\i\.Types\.REMIX/, replacement: { - match: /style:"username".{0,50}void 0/, - replace: "style:{color:$self.colorIfServer(arguments[0])}" + match: /(username,style):"username".{0,25}void 0/, + replace: "$1:{color:$self.colorIfServer(arguments[0])}" } }, { @@ -108,7 +108,8 @@ export default definePlugin({ }, colorIfServer(a: any): string | undefined { - const roleColor = a.author.colorString; + const roleColor = a.author?.colorString; + if (!roleColor) return; if (a.channel.guild_id && !settings.store.colorInServers) return roleColor; From 0d4f2677755b7a4b8ed7e6e331b1c1126e6d6016 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 14 Feb 2025 21:39:01 -0500 Subject: [PATCH 20/34] Can Reporter Stop Trolling Me :) --- src/equicordplugins/customUserColors/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx index d9ff6a71..5e0bfe35 100644 --- a/src/equicordplugins/customUserColors/index.tsx +++ b/src/equicordplugins/customUserColors/index.tsx @@ -85,8 +85,8 @@ export default definePlugin({ // this also affects name headers in chats outside of servers find: /type:\i\.\i\.Types\.REMIX/, replacement: { - match: /(username,style):"username".{0,25}void 0/, - replace: "$1:{color:$self.colorIfServer(arguments[0])}" + match: /"username".{0,25}void 0/, + replace: "{color:$self.colorIfServer(arguments[0])}" } }, { From bb5e1ec8222cb1d0ef663649941f60230699e778 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 14 Feb 2025 21:49:23 -0500 Subject: [PATCH 21/34] My Sanity ISTG --- src/equicordplugins/customUserColors/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx index 5e0bfe35..a463f10f 100644 --- a/src/equicordplugins/customUserColors/index.tsx +++ b/src/equicordplugins/customUserColors/index.tsx @@ -83,15 +83,15 @@ export default definePlugin({ patches: [ { // this also affects name headers in chats outside of servers - find: /type:\i\.\i\.Types\.REMIX/, + find: ".USERNAME),{", replacement: { - match: /"username".{0,25}void 0/, - replace: "{color:$self.colorIfServer(arguments[0])}" + match: /(\i=\{.{0,50})\?\{color:\i\}:void 0/, + replace: "color=$self.colorIfServer(arguments[0]),$1?{color:color}:{color:color}" } }, { predicate: () => settings.store.dmList, - find: /muted:\i=!1,highlighted:\i=!1/, + find: "!1,wrapContent", replacement: { match: /(nameAndDecorators,)/, replace: "$1style:{color:$self.colorDMList(arguments[0])}," From 94d45780f92b43b0dcdf6a0e2daa02334fc57d99 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 15 Feb 2025 03:27:21 -0300 Subject: [PATCH 22/34] Document new WebpackRequire properties --- src/webpack/wreq.d.ts | 74 +++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/src/webpack/wreq.d.ts b/src/webpack/wreq.d.ts index b9f5353f..ff28732c 100644 --- a/src/webpack/wreq.d.ts +++ b/src/webpack/wreq.d.ts @@ -33,27 +33,45 @@ export type AsyncModuleBody = ( asyncResult: (error?: any) => void ) => Promise; -export type ChunkHandlers = { +export type EnsureChunkHandlers = { /** * Ensures the js file for this chunk is loaded, or starts to load if it's not. * @param chunkId The chunk id * @param promises The promises array to add the loading promise to */ - j: (this: ChunkHandlers, chunkId: PropertyKey, promises: Promise) => void, + j: (this: EnsureChunkHandlers, chunkId: PropertyKey, promises: Promise) => void; /** * Ensures the css file for this chunk is loaded, or starts to load if it's not. * @param chunkId The chunk id * @param promises The promises array to add the loading promise to. This array will likely contain the promise of the js file too */ - css: (this: ChunkHandlers, chunkId: PropertyKey, promises: Promise) => void, + css: (this: EnsureChunkHandlers, chunkId: PropertyKey, promises: Promise) => void; + /** + * Trigger for prefetching next chunks. This is called after ensuring a chunk is loaded and internally looks up + * a map to see if the chunk that just loaded has next chunks to prefetch. + * + * Note that this does not add an extra promise to the promises array, and instead only executes the prefetching after + * calling Promise.all on the promises array. + * @param chunkId The chunk id + * @param promises The promises array of ensuring the chunk is loaded + */ + prefetch: (this: EnsureChunkHandlers, chunkId: PropertyKey, promises: Promise) => void; +}; + +export type PrefetchChunkHandlers = { + /** + * Prefetches the js file for this chunk. + * @param chunkId The chunk id + */ + j: (this: PrefetchChunkHandlers, chunkId: PropertyKey) => void; }; export type ScriptLoadDone = (event: Event) => void; -// export type OnChunksLoaded = ((this: WebpackRequire, result: any, chunkIds: PropertyKey[] | undefined | null, callback: () => any, priority: number) => any) & { -// /** Check if a chunk has been loaded */ -// j: (this: OnChunksLoaded, chunkId: PropertyKey) => boolean; -// }; +export type OnChunksLoaded = ((this: WebpackRequire, result: any, chunkIds: PropertyKey[] | undefined | null, callback: () => any, priority: number) => any) & { + /** Check if a chunk has been loaded */ + j: (this: OnChunksLoaded, chunkId: PropertyKey) => boolean; +}; export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & { /** The module factories, where all modules that have been loaded are stored (pre-loaded or loaded by lazy chunks) */ @@ -135,13 +153,20 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & { * // exports is now { exportName: someExportedValue } (but each value is actually a getter) */ d: (this: WebpackRequire, exports: AnyRecord, definiton: AnyRecord) => void; - /** The chunk handlers, which are used to ensure the files of the chunks are loaded, or load if necessary */ - f: ChunkHandlers; + /** The ensure chunk handlers, which are used to ensure the files of the chunks are loaded, or load if necessary */ + f: EnsureChunkHandlers; /** * The ensure chunk function, it ensures a chunk is loaded, or loads if needed. * Internally it uses the handlers in {@link WebpackRequire.f} to load/ensure the chunk is loaded. */ e: (this: WebpackRequire, chunkId: PropertyKey) => Promise; + /** The prefetch chunk handlers, which are used to prefetch the files of the chunks */ + F: PrefetchChunkHandlers; + /** + * The prefetch chunk function. + * Internally it uses the handlers in {@link WebpackRequire.F} to prefetch a chunk. + */ + E: (this: WebpackRequire, chunkId: PropertyKey) => void; /** Get the filename for the css part of a chunk */ k: (this: WebpackRequire, chunkId: PropertyKey) => string; /** Get the filename for the js part of a chunk */ @@ -162,18 +187,18 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & { r: (this: WebpackRequire, exports: ModuleExports) => void; /** Node.js module decorator. Decorates a module as a Node.js module */ nmd: (this: WebpackRequire, module: Module) => any; - // /** - // * Register deferred code which will be executed when the passed chunks are loaded. - // * - // * If chunkIds is defined, it defers the execution of the callback and returns undefined. - // * - // * If chunkIds is undefined, and no deferred code exists or can be executed, it returns the value of the result argument. - // * - // * If chunkIds is undefined, and some deferred code can already be executed, it returns the result of the callback function of the last deferred code. - // * - // * When (priority & 1) it will wait for all other handlers with lower priority to be executed before itself is executed. - // */ - // O: OnChunksLoaded; + /** + * Register deferred code which will be executed when the passed chunks are loaded. + * + * If chunkIds is defined, it defers the execution of the callback and returns undefined. + * + * If chunkIds is undefined, and no deferred code exists or can be executed, it returns the value of the result argument. + * + * If chunkIds is undefined, and some deferred code can already be executed, it returns the result of the callback function of the last deferred code. + * + * When (priority & 1) it will wait for all other handlers with lower priority to be executed before itself is executed. + */ + O: OnChunksLoaded; /** * Instantiate a wasm instance with source using "wasmModuleHash", and importObject "importsObj", and then assign the exports of its instance to "exports". * @returns The exports argument, but now assigned with the exports of the wasm instance @@ -185,6 +210,13 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & { j: string; /** Document baseURI or WebWorker location.href */ b: string; + + /* rspack only */ + + /** rspack version */ + rv: (this: WebpackRequire) => string; + /** rspack unique id */ + ruid: string; }; // Utility section for Vencord From e140784f8f0cdaaa3f23e9437ceb08e529384d15 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sat, 15 Feb 2025 12:32:52 -0500 Subject: [PATCH 23/34] LastFM EnableGameActivity --- src/plugins/lastfm/index.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/plugins/lastfm/index.tsx b/src/plugins/lastfm/index.tsx index 77fa2784..e4e9c209 100644 --- a/src/plugins/lastfm/index.tsx +++ b/src/plugins/lastfm/index.tsx @@ -17,6 +17,7 @@ */ import { definePluginSettings } from "@api/Settings"; +import { getUserSettingLazy } from "@api/UserSettings"; import { Link } from "@components/Link"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; @@ -81,6 +82,8 @@ const enum NameFormat { AlbumName = "album" } +const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame")!; + const applicationId = "1108588077900898414"; const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f"; @@ -129,6 +132,11 @@ const settings = definePluginSettings({ type: OptionType.BOOLEAN, default: false, }, + enableGameActivity: { + description: "Enable game activity for last.fm", + type: OptionType.BOOLEAN, + default: false, + }, statusName: { description: "custom status text", type: OptionType.STRING, @@ -293,6 +301,11 @@ export default definePlugin({ } const trackData = await this.fetchTrackData(); + if (settings.store.enableGameActivity && trackData) { + ShowCurrentGame.updateSetting(true); + } else if (settings.store.enableGameActivity) { + ShowCurrentGame.updateSetting(false); + } if (!trackData) return null; const largeImage = this.getLargeImage(trackData); From 5ec564558ea07b356c96de996b78e5df9ad5a6dd Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 15 Feb 2025 16:33:39 -0300 Subject: [PATCH 24/34] WebpackPatcher: Avoid patching wrong instances (like Stripe) --- src/debug/loadLazyChunks.ts | 3 +- src/debug/runReporter.ts | 3 +- src/webpack/patchWebpack.ts | 243 ++++++++++++++++++++++-------------- src/webpack/webpack.ts | 4 - 4 files changed, 152 insertions(+), 101 deletions(-) diff --git a/src/debug/loadLazyChunks.ts b/src/debug/loadLazyChunks.ts index 427acd11..9c06e3aa 100644 --- a/src/debug/loadLazyChunks.ts +++ b/src/debug/loadLazyChunks.ts @@ -20,8 +20,7 @@ export async function loadLazyChunks() { const invalidChunks = new Set(); const deferredRequires = new Set(); - let chunksSearchingResolve: (value: void) => void; - const chunksSearchingDone = new Promise(r => chunksSearchingResolve = r); + const { promise: chunksSearchingDone, resolve: chunksSearchingResolve } = Promise.withResolvers(); // True if resolved, false otherwise const chunksSearchPromises = [] as Array<() => boolean>; diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index 7a14609e..8898242e 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -17,8 +17,7 @@ async function runReporter() { try { ReporterLogger.log("Starting test..."); - let loadLazyChunksResolve: (value: void) => void; - const loadLazyChunksDone = new Promise(r => loadLazyChunksResolve = r); + const { promise: loadLazyChunksDone, resolve: loadLazyChunksResolve } = Promise.withResolvers(); // The main patch for starting the reporter chunk loading addPatch({ diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 14c1888c..9c91eee9 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -17,6 +17,7 @@ import { AnyModuleFactory, AnyWebpackRequire, MaybePatchedModuleFactory, ModuleE export const patches = [] as Patch[]; +export const SYM_IS_PROXIED_FACTORY = Symbol("WebpackPatcher.isProxiedFactory"); export const SYM_ORIGINAL_FACTORY = Symbol("WebpackPatcher.originalFactory"); export const SYM_PATCHED_SOURCE = Symbol("WebpackPatcher.patchedSource"); export const SYM_PATCHED_BY = Symbol("WebpackPatcher.patchedBy"); @@ -79,7 +80,8 @@ const define: typeof Reflect.defineProperty = (target, p, attributes) => { }; // wreq.m is the Webpack object containing module factories. It is pre-populated with factories, and is also populated via webpackGlobal.push -// We use this setter to intercept when wreq.m is defined and apply patching to its factories. +// We use this setter to intercept when wreq.m is defined and setup our setters which decide whether we should patch these module factories +// and the Webpack instance where they are being defined. // Factories can be patched in two ways. Eagerly or lazily. // If we are patching eagerly, pre-populated factories are patched immediately and new factories are patched when set. @@ -97,7 +99,7 @@ define(Function.prototype, "m", { set(this: AnyWebpackRequire, originalModules: AnyWebpackRequire["m"]) { define(this, "m", { value: originalModules }); - // Ensure this is one of Discord main Webpack instances. + // Ensure this is likely one of Discord main Webpack instances. // We may catch Discord bundled libs, React Devtools or other extensions Webpack instances here. const { stack } = new Error(); if (!stack?.includes("http") || stack.match(/at \d+? \(/) || !String(this).includes("exports:{}")) { @@ -105,53 +107,90 @@ define(Function.prototype, "m", { } const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1]; - logger.info("Found Webpack module factories" + interpolateIfDefined` in ${fileName}`); - allWebpackInstances.add(this); - - // Define a setter for the ensureChunk property of WebpackRequire. Only the main Webpack (which is the only that includes chunk loading) has this property. - // So if the setter is called, this means we can initialize the internal references to WebpackRequire. - define(this, "e", { + // Define a setter for the bundlePath property of WebpackRequire. Only Webpack instances which include chunk loading functionality, + // like the main Discord Webpack, have this property. + // So if the setter is called with the Discord bundlePath, this means we should patch this instance and initialize the internal references to WebpackRequire. + define(this, "p", { enumerable: false, - set(this: WebpackRequire, ensureChunk: WebpackRequire["e"]) { - define(this, "e", { value: ensureChunk }); - clearTimeout(setterTimeout); + set(this: AnyWebpackRequire, bundlePath: NonNullable) { + define(this, "p", { value: bundlePath }); + clearTimeout(bundlePathTimeout); - logger.info("Main WebpackInstance found" + interpolateIfDefined` in ${fileName}` + ", initializing internal references to WebpackRequire"); - _initWebpack(this); + if (bundlePath !== "/assets/") { + return; + } + + patchThisInstance(); + + if (wreq == null && this.c != null) { + logger.info("Main WebpackInstance found" + interpolateIfDefined` in ${fileName}` + ", initializing internal references to WebpackRequire"); + _initWebpack(this as WebpackRequire); + } } }); - // setImmediate to clear this property setter if this is not the main Webpack. - // If this is the main Webpack, wreq.e will always be set before the timeout runs. - const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "e"), 0); - // Patch the pre-populated factories - for (const moduleId in originalModules) { - const originalFactory = originalModules[moduleId]; + // In the past, the sentry Webpack instance which we also wanted to patch used to rely on chunks being loaded before initting sentry. + // This Webpack instance did not include actual chunk loading, and only awaited for them to be loaded, which means it did not include the bundlePath property. + // To keep backwards compability, in case this is ever the case again, and keep patching this type of instance, we explicity patch instances which include wreq.O and not wreq.p. + // Since we cannot check what is the bundlePath of the instance to filter for the Discord bundlePath, we only patch it if wreq.p is not included, + // which means the instance relies on another instance which does chunk loading, and that makes it very likely to only target Discord Webpack instances like the old sentry. - if (updateExistingFactory(originalModules, moduleId, originalFactory, originalModules, true)) { - continue; + // Instead of patching when wreq.O is defined, wait for when wreq.O.j is defined, since that will be one of the last things to happen, + // which can assure wreq.p could have already been defined before. + define(this, "O", { + enumerable: false, + + set(this: AnyWebpackRequire, onChunksLoaded: NonNullable) { + define(this, "O", { value: onChunksLoaded }); + clearTimeout(onChunksLoadedTimeout); + + const wreq = this; + define(onChunksLoaded, "j", { + enumerable: false, + + set(this: NonNullable, j: NonNullable["j"]) { + define(this, "j", { value: j }); + + if (wreq.p == null) { + patchThisInstance(); + } + } + }); } - - notifyFactoryListeners(moduleId, originalFactory); - - const proxiedFactory = new Proxy(Settings.eagerPatches ? patchFactory(moduleId, originalFactory) : originalFactory, moduleFactoryHandler); - define(originalModules, moduleId, { value: proxiedFactory }); - } - - define(originalModules, Symbol.toStringTag, { - value: "ModuleFactories", - enumerable: false }); - const proxiedModuleFactories = new Proxy(originalModules, moduleFactoriesHandler); - /* - If Webpack ever decides to set module factories using the variable of the modules object directly, instead of wreq.m, switch the proxy to the prototype - Reflect.setPrototypeOf(originalModules, new Proxy(originalModules, moduleFactoriesHandler)); - */ + // If neither of these properties setters were triggered, delete them as they are not needed anymore. + const bundlePathTimeout = setTimeout(() => Reflect.deleteProperty(this, "p"), 0); + const onChunksLoadedTimeout = setTimeout(() => Reflect.deleteProperty(this, "O"), 0); - define(this, "m", { value: proxiedModuleFactories }); + /** + * Patch the current Webpack instance assigned to `this` context. + * This should only be called if this instance was later found to be one we need to patch. + */ + const patchThisInstance = () => { + logger.info("Found Webpack module factories" + interpolateIfDefined` in ${fileName}`); + allWebpackInstances.add(this); + + // Proxy (and maybe patch) pre-populated factories + for (const moduleId in originalModules) { + updateExistingOrProxyFactory(originalModules, moduleId, originalModules[moduleId], originalModules, true); + } + + define(originalModules, Symbol.toStringTag, { + value: "ModuleFactories", + enumerable: false + }); + + const proxiedModuleFactories = new Proxy(originalModules, moduleFactoriesHandler); + /* + If Webpack ever decides to set module factories using the variable of the modules object directly, instead of wreq.m, switch the proxy to the prototype + Reflect.setPrototypeOf(originalModules, new Proxy(originalModules, moduleFactoriesHandler)); + */ + + define(this, "m", { value: proxiedModuleFactories }); + }; } }); @@ -172,93 +211,102 @@ const moduleFactoriesHandler: ProxyHandler = { }, */ - set(target, p, newValue, receiver) { - if (updateExistingFactory(target, p, newValue, receiver)) { - return true; - } - - notifyFactoryListeners(p, newValue); - - const proxiedFactory = new Proxy(Settings.eagerPatches ? patchFactory(p, newValue) : newValue, moduleFactoryHandler); - return Reflect.set(target, p, proxiedFactory, receiver); - } + set: updateExistingOrProxyFactory }; // The proxy for patching lazily and/or running factories with our wrapper. const moduleFactoryHandler: ProxyHandler = { apply(target, thisArg: unknown, argArray: Parameters) { - // SAFETY: Factories have `name` as their key in the module factories object, and that is always their module id - const moduleId = target.name; - // SYM_ORIGINAL_FACTORY means the factory has already been patched if (target[SYM_ORIGINAL_FACTORY] != null) { - return runFactoryWithWrap(moduleId, target as PatchedModuleFactory, thisArg, argArray); + return runFactoryWithWrap(target as PatchedModuleFactory, thisArg, argArray); } + // SAFETY: Factories have `name` as their key in the module factories object, and that is always their module id + const moduleId: string = target.name; + const patchedFactory = patchFactory(moduleId, target); - return runFactoryWithWrap(moduleId, patchedFactory, thisArg, argArray); + return runFactoryWithWrap(patchedFactory, thisArg, argArray); }, get(target, p, receiver) { - if (target[SYM_ORIGINAL_FACTORY] != null && (p === SYM_PATCHED_SOURCE || p === SYM_PATCHED_BY)) { - return Reflect.get(target[SYM_ORIGINAL_FACTORY], p, target[SYM_ORIGINAL_FACTORY]); + if (p === SYM_IS_PROXIED_FACTORY) { + return true; } - const v = Reflect.get(target, p, receiver); + const originalFactory: AnyModuleFactory = target[SYM_ORIGINAL_FACTORY] ?? target; - // Make proxied factories `toString` return their original factory `toString` - if (p === "toString") { - return v.bind(target[SYM_ORIGINAL_FACTORY] ?? target); + // Redirect these properties to the original factory, including making `toString` return the original factory `toString` + if (p === "toString" || p === SYM_PATCHED_SOURCE || p === SYM_PATCHED_BY) { + const v = Reflect.get(originalFactory, p, originalFactory); + return p === "toString" ? v.bind(originalFactory) : v; } - return v; + return Reflect.get(target, p, receiver); } }; +function updateExistingOrProxyFactory(moduleFactories: AnyWebpackRequire["m"], moduleId: PropertyKey, newFactory: AnyModuleFactory, receiver: any, ignoreExistingInTarget = false) { + if (updateExistingFactory(moduleFactories, moduleId, newFactory, receiver, ignoreExistingInTarget)) { + return true; + } + + notifyFactoryListeners(moduleId, newFactory); + + const proxiedFactory = new Proxy(Settings.eagerPatches ? patchFactory(moduleId, newFactory) : newFactory, moduleFactoryHandler); + return Reflect.set(moduleFactories, moduleId, proxiedFactory, receiver); +} + /** - * Update a factory that exists in any Webpack instance with a new original factory. + * Update a duplicated factory that exists in any of the Webpack instances we track with a new original factory. * - * @param moduleFactoriesTarget The module factories where this new original factory is being set + * @param moduleFactories The module factories where this new original factory is being set * @param moduleId The id of the module * @param newFactory The new original factory - * @param receiver The receiver of the new factory - * @param ignoreExistingInTarget Whether to ignore checking if the factory already exists in the moduleFactoriesTarget - * @returns Whether the original factory was updated, or false if it doesn't exist in any Webpack instance + * @param receiver The receiver of the factory + * @param ignoreExistingInTarget Whether to ignore checking if the factory already exists in the moduleFactories where it is being set + * @returns Whether the original factory was updated, or false if it doesn't exist in any of the tracked Webpack instances */ -function updateExistingFactory(moduleFactoriesTarget: AnyWebpackRequire["m"], moduleId: PropertyKey, newFactory: AnyModuleFactory, receiver: any, ignoreExistingInTarget: boolean = false) { - let existingFactory: TypedPropertyDescriptor | undefined; +function updateExistingFactory(moduleFactories: AnyWebpackRequire["m"], moduleId: PropertyKey, newFactory: AnyModuleFactory, receiver: any, ignoreExistingInTarget) { + let existingFactory: AnyModuleFactory | undefined; let moduleFactoriesWithFactory: AnyWebpackRequire["m"] | undefined; for (const wreq of allWebpackInstances) { - if (ignoreExistingInTarget && wreq.m === moduleFactoriesTarget) { + if (ignoreExistingInTarget && wreq.m === moduleFactories) { continue; } if (Object.hasOwn(wreq.m, moduleId)) { - existingFactory = Reflect.getOwnPropertyDescriptor(wreq.m, moduleId); + existingFactory = wreq.m[moduleId]; moduleFactoriesWithFactory = wreq.m; break; } } if (existingFactory != null) { - // If existingFactory exists in any Webpack instance, it's either wrapped in our proxy, or it has already been required. - // In the case it is wrapped in our proxy, we need the Webpack instance with this new original factory to also have our proxy. - // So, define the descriptor of the existing factory on it. - if (moduleFactoriesWithFactory !== moduleFactoriesTarget) { - Reflect.defineProperty(receiver, moduleId, existingFactory); + // Sanity check to make sure these factories are equal + if (String(newFactory) !== String(existingFactory)) { + return false; } - const existingFactoryValue = moduleFactoriesWithFactory![moduleId]; + // If existingFactory exists in any of the Webpack instances we track, it's either wrapped in our proxy, or it has already been required. + // In the case it is wrapped in our proxy, and the instance we are setting does not already have it, we need to make sure the instance contains our proxy too. + if (moduleFactoriesWithFactory !== moduleFactories && existingFactory[SYM_IS_PROXIED_FACTORY]) { + Reflect.set(moduleFactories, moduleId, existingFactory, receiver); + } + // Else, if it is not wrapped in our proxy, set this new original factory in all the instances + else { + defineInWebpackInstances(moduleId, newFactory); + } - // Update with the new original factory, if it does have a current original factory - if (existingFactoryValue[SYM_ORIGINAL_FACTORY] != null) { - existingFactoryValue[SYM_ORIGINAL_FACTORY] = newFactory; + // Update existingFactory with the new original, if it does have a current original factory + if (existingFactory[SYM_ORIGINAL_FACTORY] != null) { + existingFactory[SYM_ORIGINAL_FACTORY] = newFactory; } // Persist patched source and patched by in the new original factory if (IS_DEV) { - newFactory[SYM_PATCHED_SOURCE] = existingFactoryValue[SYM_PATCHED_SOURCE]; - newFactory[SYM_PATCHED_BY] = existingFactoryValue[SYM_PATCHED_BY]; + newFactory[SYM_PATCHED_SOURCE] = existingFactory[SYM_PATCHED_SOURCE]; + newFactory[SYM_PATCHED_BY] = existingFactory[SYM_PATCHED_BY]; } return true; @@ -267,6 +315,18 @@ function updateExistingFactory(moduleFactoriesTarget: AnyWebpackRequire["m"], mo return false; } +/** + * Define a module factory in all the Webpack instances we track. + * + * @param moduleId The id of the module + * @param factory The factory + */ +function defineInWebpackInstances(moduleId: PropertyKey, factory: AnyModuleFactory) { + for (const wreq of allWebpackInstances) { + define(wreq.m, moduleId, { value: factory }); + } +} + /** * Notify all factory listeners. * @@ -286,12 +346,11 @@ function notifyFactoryListeners(moduleId: PropertyKey, factory: AnyModuleFactory /** * Run a (possibly) patched module factory with a wrapper which notifies our listeners. * - * @param moduleId The id of the module * @param patchedFactory The (possibly) patched module factory * @param thisArg The `value` of the call to the factory * @param argArray The arguments of the call to the factory */ -function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModuleFactory, thisArg: unknown, argArray: Parameters) { +function runFactoryWithWrap(patchedFactory: PatchedModuleFactory, thisArg: unknown, argArray: Parameters) { const originalFactory = patchedFactory[SYM_ORIGINAL_FACTORY]; if (patchedFactory === originalFactory) { @@ -299,25 +358,23 @@ function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModule delete patchedFactory[SYM_ORIGINAL_FACTORY]; } - // Restore the original factory in all the module factories objects, discarding our proxy and allowing it to be garbage collected - for (const wreq of allWebpackInstances) { - define(wreq.m, moduleId, { value: originalFactory }); - } - let [module, exports, require] = argArray; + // Restore the original factory in all the module factories objects, discarding our proxy and allowing it to be garbage collected + defineInWebpackInstances(module.id, originalFactory); + if (wreq == null) { if (!wreqFallbackApplied) { wreqFallbackApplied = true; // Make sure the require argument is actually the WebpackRequire function - if (typeof require === "function" && require.m != null) { + if (typeof require === "function" && require.m != null && require.c != null) { const { stack } = new Error(); const webpackInstanceFileName = stack?.match(/\/assets\/(.+?\.js)/)?.[1]; logger.warn( "WebpackRequire was not initialized, falling back to WebpackRequire passed to the first called wrapped module factory (" + - `id: ${String(moduleId)}` + interpolateIfDefined`, WebpackInstance origin: ${webpackInstanceFileName}` + + `id: ${String(module.id)}` + interpolateIfDefined`, WebpackInstance origin: ${webpackInstanceFileName}` + ")" ); @@ -355,8 +412,8 @@ function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModule if (shouldIgnoreModule) { if (require.c != null) { - Object.defineProperty(require.c, moduleId, { - value: require.c[moduleId], + Object.defineProperty(require.c, module.id, { + value: require.c[module.id], enumerable: false, configurable: true, writable: true @@ -369,7 +426,7 @@ function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModule for (const callback of moduleListeners) { try { - callback(exports, moduleId); + callback(exports, module.id); } catch (err) { logger.error("Error in Webpack module listener:\n", err, callback); } @@ -379,7 +436,7 @@ function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModule try { if (filter(exports)) { waitForSubscriptions.delete(filter); - callback(exports, moduleId); + callback(exports, module.id); continue; } @@ -392,7 +449,7 @@ function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModule if (exportValue != null && filter(exportValue)) { waitForSubscriptions.delete(filter); - callback(exportValue, moduleId); + callback(exportValue, module.id); break; } } diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index 09c04a2a..fe939d9b 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -97,10 +97,6 @@ export const moduleListeners = new Set(); export const factoryListeners = new Set(); export function _initWebpack(webpackRequire: WebpackRequire) { - if (webpackRequire.c == null) { - return; - } - wreq = webpackRequire; cache = webpackRequire.c; From 53eb329cbc306d473f1ba8cc84dd0d66d823f75b Mon Sep 17 00:00:00 2001 From: mochie Date: Sun, 16 Feb 2025 15:38:05 +0100 Subject: [PATCH 25/34] optionally remove shift req. from showAllMessageButtons (#151) --- src/plugins/showAllMessageButtons/index.ts | 79 +++++++++++++++------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/src/plugins/showAllMessageButtons/index.ts b/src/plugins/showAllMessageButtons/index.ts index 1d689013..10069a58 100644 --- a/src/plugins/showAllMessageButtons/index.ts +++ b/src/plugins/showAllMessageButtons/index.ts @@ -1,36 +1,65 @@ /* - * 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 . -*/ + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ -import { Devs } from "@utils/constants"; -import definePlugin from "@utils/types"; +import { definePluginSettings } from "@api/Settings"; +import { Devs, EquicordDevs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { findByPropsLazy } from "@webpack"; +import { MessageActions } from "@webpack/common"; + +const settings = definePluginSettings({ + noShiftDelete: { + type: OptionType.BOOLEAN, + description: "Remove requirement to hold shift for deleting a message.", + default: true, + }, + noShiftPin: { + type: OptionType.BOOLEAN, + description: "Remove requirement to hold shift for pinning a message.", + default: true, + }, +}); + +const PinActions = findByPropsLazy("pinMessage", "unpinMessage"); export default definePlugin({ name: "ShowAllMessageButtons", description: "Always show all message buttons no matter if you are holding the shift key or not.", - authors: [Devs.Nuckyz], + authors: [Devs.Nuckyz, EquicordDevs.mochienya], + settings, patches: [ { find: "#{intl::MESSAGE_UTILITIES_A11Y_LABEL}", - replacement: { - match: /isExpanded:\i&&(.+?),/, - replace: "isExpanded:$1," - } - } - ] + replacement: [ + { + match: /isExpanded:\i&&(.+?),/, + replace: "isExpanded:$1," + }, + { + predicate: () => settings.store.noShiftDelete, + match: /onClick:.{10,20}(?=,dangerous:!0)/, + replace: "onClick:() => $self.deleteMessage(arguments[0].message)", + }, + { + predicate: () => settings.store.noShiftPin, + match: /onClick:.{10,30}(?=\},"pin")/, + replace: "onClick:() => $self.toggleMessagePin(arguments[0])," + } + ] + }, + ], + + deleteMessage({ channel_id, id }) { + MessageActions.deleteMessage(channel_id, id); + }, + toggleMessagePin({ channel, message }) { + if (message.pinned) + return PinActions.unpinMessage(channel, message.id); + + PinActions.pinMessage(channel, message.id); + }, }); From 8dfbb5f9d887b7135f73cfd0a262c5ba1826e9f4 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sun, 16 Feb 2025 16:54:35 -0300 Subject: [PATCH 26/34] Make findStore use FluxStore.getAll instead of webpack searching --- src/webpack/webpack.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index fe939d9b..e129b913 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -22,6 +22,7 @@ import { Logger } from "@utils/Logger"; import { canonicalizeMatch } from "@utils/patches"; import { traceFunction } from "../debug/Tracer"; +import { Flux } from "./common"; import { AnyModuleFactory, ModuleExports, WebpackRequire } from "./wreq"; const logger = new Logger("Webpack"); @@ -405,7 +406,7 @@ export function findByCodeLazy(...code: CodeFilter) { * Find a store by its displayName */ export function findStore(name: StoreNameFilter) { - const res = find(filters.byStoreName(name), { isIndirect: true }); + const res: any = Flux.Store.getAll().find(filters.byStoreName(name)); if (!res) handleModuleNotFound("findStore", name); return res; From 2f3bda934e614f0fb767b74a69fa728d53d729d7 Mon Sep 17 00:00:00 2001 From: Cassie <37855219+CodeF53@users.noreply.github.com> Date: Sun, 16 Feb 2025 17:52:54 -0700 Subject: [PATCH 27/34] Fix CustomUserColors (#152) * fix the code equicord devs (not discord) broke * Spacing --------- Co-authored-by: thororen <78185467+thororen1234@users.noreply.github.com> --- src/equicordplugins/customUserColors/index.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx index a463f10f..6207e05b 100644 --- a/src/equicordplugins/customUserColors/index.tsx +++ b/src/equicordplugins/customUserColors/index.tsx @@ -85,8 +85,8 @@ export default definePlugin({ // this also affects name headers in chats outside of servers find: ".USERNAME),{", replacement: { - match: /(\i=\{.{0,50})\?\{color:\i\}:void 0/, - replace: "color=$self.colorIfServer(arguments[0]),$1?{color:color}:{color:color}" + match: /style:"username"===.{0,25}void 0/, + replace: "style:{color:$self.colorIfServer(arguments[0])}" } }, { @@ -108,8 +108,7 @@ export default definePlugin({ }, colorIfServer(a: any): string | undefined { - const roleColor = a.author?.colorString; - if (!roleColor) return; + const roleColor = a.author?.colorString ?? "inherit"; if (a.channel.guild_id && !settings.store.colorInServers) return roleColor; From 6b0c02daa63a626698ab58310b024d666c406e1a Mon Sep 17 00:00:00 2001 From: v Date: Mon, 17 Feb 2025 02:10:02 +0100 Subject: [PATCH 28/34] fix issues & crashes caused by recent Discord changes (#3231) Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com> --- src/debug/runReporter.ts | 4 +- src/plugins/_core/settings.tsx | 3 ++ src/webpack/common/utils.ts | 4 +- src/webpack/patchWebpack.ts | 31 ++++++------ src/webpack/webpack.ts | 86 +++++++++++++++++++++++----------- 5 files changed, 82 insertions(+), 46 deletions(-) diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index 8898242e..2ca83b7f 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -78,9 +78,9 @@ async function runReporter() { result = await Webpack.extractAndLoadChunks(code, matcher); if (result === false) result = null; } else if (method === "mapMangledModule") { - const [code, mapper] = args; + const [code, mapper, includeBlacklistedExports] = args; - result = Webpack.mapMangledModule(code, mapper); + result = Webpack.mapMangledModule(code, mapper, includeBlacklistedExports); if (Object.keys(result).length !== Object.keys(mapper).length) throw new Error("Webpack Find Fail"); } else { // @ts-ignore diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index f48f38e0..a9e34f78 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -158,6 +158,9 @@ export default definePlugin({ aboveActivity: getIntlMessage("ACTIVITY_SETTINGS") }; + if (!names[settingsLocation] || names[settingsLocation].endsWith("_SETTINGS")) + return firstChild === "PREMIUM"; + return header === names[settingsLocation]; } catch { return firstChild === "PREMIUM"; diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index fe066888..9ed1489c 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -58,9 +58,9 @@ export const { match, P }: Pick = ma export const lodash: typeof import("lodash") = findByPropsLazy("debounce", "cloneDeep"); export const i18n = mapMangledModuleLazy('defaultLocale:"en-US"', { + t: filters.byProps(runtimeHashMessageKey("DISCORD")), intl: filters.byProps("string", "format"), - t: filters.byProps(runtimeHashMessageKey("DISCORD")) -}); +}, true); export let SnowflakeUtils: t.SnowflakeUtils; waitFor(["fromTimestamp", "extractTimestamp"], m => SnowflakeUtils = m); diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 9c91eee9..408d3b70 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -12,7 +12,7 @@ import { canonicalizeReplacement } from "@utils/patches"; import { Patch, PatchReplacement } from "@utils/types"; import { traceFunctionWithResults } from "../debug/Tracer"; -import { _initWebpack, _shouldIgnoreModule, factoryListeners, findModuleId, moduleListeners, waitForSubscriptions, wreq } from "./webpack"; +import { _blacklistBadModules, _initWebpack, factoryListeners, findModuleId, moduleListeners, waitForSubscriptions, wreq } from "./webpack"; import { AnyModuleFactory, AnyWebpackRequire, MaybePatchedModuleFactory, ModuleExports, PatchedModuleFactory, WebpackRequire } from "./wreq.d"; export const patches = [] as Patch[]; @@ -190,6 +190,20 @@ define(Function.prototype, "m", { */ define(this, "m", { value: proxiedModuleFactories }); + + // Overwrite Webpack's defineExports function to define the export descriptors configurable. + // This is needed so we can later blacklist specific exports from Webpack search by making them non-enumerable + this.d = function (exports: object, getters: object) { + for (const key in getters) { + if (Object.hasOwn(getters, key) && !Object.hasOwn(exports, key)) { + Object.defineProperty(exports, key, { + enumerable: true, + configurable: true, + get: getters[key], + }); + } + } + }; }; } }); @@ -407,19 +421,8 @@ function runFactoryWithWrap(patchedFactory: PatchedModuleFactory, thisArg: unkno return factoryReturn; } - if (typeof require === "function") { - const shouldIgnoreModule = _shouldIgnoreModule(exports); - - if (shouldIgnoreModule) { - if (require.c != null) { - Object.defineProperty(require.c, module.id, { - value: require.c[module.id], - enumerable: false, - configurable: true, - writable: true - }); - } - + if (typeof require === "function" && require.c) { + if (_blacklistBadModules(require.c, exports, module.id)) { return factoryReturn; } } diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index e129b913..30180a7e 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -23,7 +23,7 @@ import { canonicalizeMatch } from "@utils/patches"; import { traceFunction } from "../debug/Tracer"; import { Flux } from "./common"; -import { AnyModuleFactory, ModuleExports, WebpackRequire } from "./wreq"; +import { AnyModuleFactory, AnyWebpackRequire, ModuleExports, WebpackRequire } from "./wreq"; const logger = new Logger("Webpack"); @@ -112,18 +112,38 @@ export function _initWebpack(webpackRequire: WebpackRequire) { // Credits to Zerebos for implementing this in BD, thus giving the idea for us to implement it too const TypedArray = Object.getPrototypeOf(Int8Array); -function _shouldIgnoreValue(value: any) { +const PROXY_CHECK = "is this a proxy that returns values for any key?"; +function shouldIgnoreValue(value: any) { if (value == null) return true; if (value === window) return true; if (value === document || value === document.documentElement) return true; - if (value[Symbol.toStringTag] === "DOMTokenList") return true; + if (value[Symbol.toStringTag] === "DOMTokenList" || value[Symbol.toStringTag] === "IntlMessagesProxy") return true; + // Discord might export a Proxy that returns non-null values for any property key which would pass all findByProps filters. + // One example of this is their i18n Proxy. However, that is already covered by the IntlMessagesProxy check above. + // As a fallback if they ever change the name or add a new Proxy, use a unique string to detect such proxies and ignore them + if (value[PROXY_CHECK] !== void 0) { + // their i18n Proxy "caches" by setting each accessed property to the return, so try to delete + Reflect.deleteProperty(value, PROXY_CHECK); + return true; + } if (value instanceof TypedArray) return true; return false; } -export function _shouldIgnoreModule(exports: any) { - if (_shouldIgnoreValue(exports)) { +function makePropertyNonEnumerable(target: Object, key: PropertyKey) { + const descriptor = Object.getOwnPropertyDescriptor(target, key); + if (descriptor == null) return; + + Reflect.defineProperty(target, key, { + ...descriptor, + enumerable: false + }); +} + +export function _blacklistBadModules(requireCache: NonNullable, exports: ModuleExports, moduleId: PropertyKey) { + if (shouldIgnoreValue(exports)) { + makePropertyNonEnumerable(requireCache, moduleId); return true; } @@ -131,14 +151,16 @@ export function _shouldIgnoreModule(exports: any) { return false; } - let allNonEnumerable = true; + let hasOnlyBadProperties = true; for (const exportKey in exports) { - if (!_shouldIgnoreValue(exports[exportKey])) { - allNonEnumerable = false; + if (shouldIgnoreValue(exports[exportKey])) { + makePropertyNonEnumerable(exports, exportKey); + } else { + hasOnlyBadProperties = false; } } - return allNonEnumerable; + return hasOnlyBadProperties; } let devToolsOpen = false; @@ -406,7 +428,10 @@ export function findByCodeLazy(...code: CodeFilter) { * Find a store by its displayName */ export function findStore(name: StoreNameFilter) { - const res: any = Flux.Store.getAll().find(filters.byStoreName(name)); + const res = Flux.Store.getAll + ? Flux.Store.getAll().find(filters.byStoreName(name)) + : find(filters.byStoreName(name), { isIndirect: true }); + if (!res) handleModuleNotFound("findStore", name); return res; @@ -474,12 +499,27 @@ export function findExportedComponentLazy(...props: Prop }); } +function getAllPropertyNames(object: Object, includeNonEnumerable: boolean) { + const names = new Set(); + + const getKeys = includeNonEnumerable ? Object.getOwnPropertyNames : Object.keys; + do { + getKeys(object).forEach(name => names.add(name)); + object = Object.getPrototypeOf(object); + } while (object != null); + + return names; +} + /** * Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module) * then maps it into an easily usable module via the specified mappers. * * @param code The code to look for * @param mappers Mappers to create the non mangled exports + * @param includeBlacklistedExports Whether to include blacklisted exports in the search. + * These exports are dangerous. Accessing properties on them may throw errors + * or always return values (so a byProps filter will always return true) * @returns Unmangled exports as specified in mappers * * @example mapMangledModule("headerIdIsManaged:", { @@ -487,7 +527,7 @@ export function findExportedComponentLazy(...props: Prop * closeModal: filters.byCode("key==") * }) */ -export const mapMangledModule = traceFunction("mapMangledModule", function mapMangledModule(code: string | RegExp | CodeFilter, mappers: Record): Record { +export const mapMangledModule = traceFunction("mapMangledModule", function mapMangledModule(code: string | RegExp | CodeFilter, mappers: Record, includeBlacklistedExports = false): Record { const exports = {} as Record; const id = findModuleId(...Array.isArray(code) ? code : [code]); @@ -495,8 +535,9 @@ export const mapMangledModule = traceFunction("mapMangledModule", function mapMa return exports; const mod = wreq(id as any); + const keys = getAllPropertyNames(mod, includeBlacklistedExports); outer: - for (const key in mod) { + for (const key of keys) { const member = mod[key]; for (const newName in mappers) { // if the current mapper matches this module @@ -510,24 +551,13 @@ export const mapMangledModule = traceFunction("mapMangledModule", function mapMa }); /** - * {@link mapMangledModule}, lazy. - - * Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module) - * then maps it into an easily usable module via the specified mappers. - * - * @param code The code to look for - * @param mappers Mappers to create the non mangled exports - * @returns Unmangled exports as specified in mappers - * - * @example mapMangledModule("headerIdIsManaged:", { - * openModal: filters.byCode("headerIdIsManaged:"), - * closeModal: filters.byCode("key==") - * }) + * lazy mapMangledModule + * @see {@link mapMangledModule} */ -export function mapMangledModuleLazy(code: string | RegExp | CodeFilter, mappers: Record): Record { - if (IS_REPORTER) lazyWebpackSearchHistory.push(["mapMangledModule", [code, mappers]]); +export function mapMangledModuleLazy(code: string | RegExp | CodeFilter, mappers: Record, includeBlacklistedExports = false): Record { + if (IS_REPORTER) lazyWebpackSearchHistory.push(["mapMangledModule", [code, mappers, includeBlacklistedExports]]); - return proxyLazy(() => mapMangledModule(code, mappers)); + return proxyLazy(() => mapMangledModule(code, mappers, includeBlacklistedExports)); } export const DefaultExtractAndLoadChunksRegex = /(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/; From 57c31a6e7ccf8457217cb279418ab28504eadb4f Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sun, 16 Feb 2025 20:15:39 -0500 Subject: [PATCH 29/34] Fix InitWS --- src/plugins/devCompanion.dev/initWs.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/devCompanion.dev/initWs.tsx b/src/plugins/devCompanion.dev/initWs.tsx index 0d22dfc5..36ee4778 100644 --- a/src/plugins/devCompanion.dev/initWs.tsx +++ b/src/plugins/devCompanion.dev/initWs.tsx @@ -337,7 +337,7 @@ export function initWs(isManual = false) { try { const matcher = canonicalizeMatch(parseNode(match)); - const replacement = canonicalizeReplace(parseNode(replace), "PlaceHolderPluginName"); + const replacement = canonicalizeReplace(parseNode(replace), 'Vencord.Plugins.plugins["PlaceHolderPluginName"]'); const newSource = src.replace(matcher, replacement as string); From 2859333945602802477725032f85bb4b48a78916 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sun, 16 Feb 2025 20:16:41 -0500 Subject: [PATCH 30/34] Ignore CustomUserColors Patches --- src/equicordplugins/customUserColors/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx index 6207e05b..16349162 100644 --- a/src/equicordplugins/customUserColors/index.tsx +++ b/src/equicordplugins/customUserColors/index.tsx @@ -11,7 +11,7 @@ import { get } from "@api/DataStore"; import { definePluginSettings, migratePluginSettings, Settings } from "@api/Settings"; import { EquicordDevs } from "@utils/constants"; import { openModal } from "@utils/modal"; -import definePlugin, { OptionType } from "@utils/types"; +import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; import { extractAndLoadChunksLazy } from "@webpack"; import { Menu } from "@webpack/common"; import { User } from "discord-types/general"; @@ -76,6 +76,7 @@ export default definePlugin({ description: "Lets you add a custom color to any user, anywhere! Highly recommend to use with typingTweaks and roleColorEverywhere", authors: [EquicordDevs.mochienya], contextMenus: { "user-context": userContextMenuPatch }, + reporterTestable: ReporterTestable.None, settings, requireSettingsMenu, getCustomColorString, From 35f28d2d375788fdff8bf2267787935ab503a68b Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sun, 16 Feb 2025 20:18:28 -0500 Subject: [PATCH 31/34] Fix FontLoader Name PT 1 --- src/equicordplugins/{FontLoader => fontLoad}/index.tsx | 0 src/equicordplugins/{FontLoader => fontLoad}/styles.css | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/equicordplugins/{FontLoader => fontLoad}/index.tsx (100%) rename src/equicordplugins/{FontLoader => fontLoad}/styles.css (100%) diff --git a/src/equicordplugins/FontLoader/index.tsx b/src/equicordplugins/fontLoad/index.tsx similarity index 100% rename from src/equicordplugins/FontLoader/index.tsx rename to src/equicordplugins/fontLoad/index.tsx diff --git a/src/equicordplugins/FontLoader/styles.css b/src/equicordplugins/fontLoad/styles.css similarity index 100% rename from src/equicordplugins/FontLoader/styles.css rename to src/equicordplugins/fontLoad/styles.css From 28c330fa7301c400133b4585cb767b78ade4573c Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Sun, 16 Feb 2025 20:18:40 -0500 Subject: [PATCH 32/34] PT 2 --- src/equicordplugins/{fontLoad => fontLoader}/index.tsx | 0 src/equicordplugins/{fontLoad => fontLoader}/styles.css | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/equicordplugins/{fontLoad => fontLoader}/index.tsx (100%) rename src/equicordplugins/{fontLoad => fontLoader}/styles.css (100%) diff --git a/src/equicordplugins/fontLoad/index.tsx b/src/equicordplugins/fontLoader/index.tsx similarity index 100% rename from src/equicordplugins/fontLoad/index.tsx rename to src/equicordplugins/fontLoader/index.tsx diff --git a/src/equicordplugins/fontLoad/styles.css b/src/equicordplugins/fontLoader/styles.css similarity index 100% rename from src/equicordplugins/fontLoad/styles.css rename to src/equicordplugins/fontLoader/styles.css From f91e2ca8740cdc489b704a3bc9c01b6bff32327f Mon Sep 17 00:00:00 2001 From: Vendicated Date: Mon, 17 Feb 2025 02:41:30 +0100 Subject: [PATCH 33/34] bump to v1.11.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 872d7ce0..2f88ec1f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.11.4", + "version": "1.11.5", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 1f6720318354e026fe24ed3e76935ec9e42d5193 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Mon, 17 Feb 2025 02:46:59 +0100 Subject: [PATCH 34/34] fix ci --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ba22b123..b1cbc302 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: - name: Clean up obsolete files run: | - rm -rf dist/*-unpacked dist/monaco Vencord.user.css vencordDesktopRenderer.css vencordDesktopRenderer.css.map + rm -rf dist/*-unpacked dist/vendor Vencord.user.css vencordDesktopRenderer.css vencordDesktopRenderer.css.map - name: Get some values needed for the release id: release_values