Webpack Warnings & Errors (#178)

* dev: Useful strict Warnings & Errors

* Always log error

* Ignore pending patches with all or whose predicate = false

* Error -> Warn
This commit is contained in:
Ven 2022-10-30 20:45:18 +01:00 committed by GitHub
parent b905743077
commit 8adf7ca155
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 90 additions and 55 deletions

@ -25,7 +25,7 @@ export * as Webpack from "./webpack";
import { popNotice, showNotice } from "./api/Notices";
import { PlainSettings, Settings } from "./api/settings";
import { startAllPlugins } from "./plugins";
import { patches, PMLogger, startAllPlugins } from "./plugins";
export { PlainSettings, Settings };
@ -61,6 +61,19 @@ async function init() {
UpdateLogger.error("Failed to check for updates", err);
}
}
if (IS_DEV) {
const pendingPatches = patches.filter(p => !p.all && p.predicate?.() !== false);
if (pendingPatches.length)
PMLogger.warn(
"Webpack has finished initialising, but some patches haven't been applied yet.",
"This might be expected since some Modules are lazy loaded, but please verify",
"that all plugins are working as intended.",
"You are seeing this warning because this is a Development build of Vencord.",
"\nThe following patches have not been applied:",
"\n\n" + pendingPatches.map(p => `${p.plugin}: ${p.find}`).join("\n")
);
}
}
init();

@ -24,7 +24,7 @@ import { filters, waitFor } from "../../webpack";
import { Argument } from "./types";
const createBotMessage = lazyWebpack(filters.byCode('username:"Clyde"'));
const MessageSender = lazyWebpack(filters.byProps(["receiveMessage"]));
const MessageSender = lazyWebpack(filters.byProps("receiveMessage"));
let SnowflakeUtils: any;
waitFor("fromTimestamp", m => SnowflakeUtils = m);

@ -38,7 +38,7 @@ import {
} from "./components";
const UserSummaryItem = lazyWebpack(filters.byCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
const AvatarStyles = lazyWebpack(filters.byProps(["moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"]));
const AvatarStyles = lazyWebpack(filters.byProps("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"));
const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any;
interface PluginModalProps extends ModalProps {

@ -36,7 +36,7 @@ import * as styles from "./styles";
const logger = new Logger("PluginSettings", "#a6d189");
const Select = lazyWebpack(filters.byCode("optionClassName", "popoutPosition", "autoFocus", "maxVisibleItems"));
const InputStyles = lazyWebpack(filters.byProps(["inputDefault", "inputWrapper"]));
const InputStyles = lazyWebpack(filters.byProps("inputDefault", "inputWrapper"));
const CogWheel = lazyWebpack(filters.byCode("18.564C15.797 19.099 14.932 19.498 14 19.738V22H10V19.738C9.069"));
const InfoIcon = lazyWebpack(filters.byCode("4.4408921e-16 C4.4771525,-1.77635684e-15 4.4408921e-16"));

@ -23,7 +23,7 @@ import { Settings } from "../Vencord";
import { filters } from "../webpack";
import { Forms, React } from "../webpack/common";
const KbdStyles = lazyWebpack(filters.byProps(["key", "removeBuildOverride"]));
const KbdStyles = lazyWebpack(filters.byProps("key", "removeBuildOverride"));
export default definePlugin({
name: "Experiments",

@ -28,7 +28,7 @@ interface MatchAndReplace {
}
/** Used to re-render the Registered Games tab to update how our button looks like */
const RunningGameStoreModule = lazyWebpack(filters.byProps(["IgnoreActivities_reRenderGames"]));
const RunningGameStoreModule = lazyWebpack(filters.byProps("IgnoreActivities_reRenderGames"));
let ignoredActivitiesCache: string[] = [];

@ -25,6 +25,7 @@ import { Patch, Plugin } from "../utils/types";
const logger = new Logger("PluginManager", "#a6d189");
export const PMLogger = logger;
export const plugins = Plugins;
export const patches = [] as Patch[];

@ -24,7 +24,7 @@ import definePlugin from "../utils/types";
import { filters } from "../webpack";
import { ChannelStore, FluxDispatcher as Dispatcher, SelectedChannelStore, UserStore } from "../webpack/common";
const MessageStore = lazyWebpack(filters.byProps(["getRawMessages"]));
const MessageStore = lazyWebpack(filters.byProps("getRawMessages"));
const isMac = navigator.platform.includes("Mac"); // bruh
let replyIdx = -1;

@ -16,9 +16,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { ApplicationCommandInputType, ApplicationCommandOptionType, Argument, CommandContext,findOption } from "../api/Commands";
import { ApplicationCommandInputType, ApplicationCommandOptionType, Argument, CommandContext, findOption } from "../api/Commands";
import { Devs } from "../utils/constants";
import { lazyWebpack,makeLazy } from "../utils/misc";
import { lazyWebpack, makeLazy } from "../utils/misc";
import definePlugin from "../utils/types";
import { filters } from "../webpack";
@ -41,7 +41,7 @@ const getFrames = makeLazy(() => Promise.all(
const fetchUser = lazyWebpack(filters.byCode(".USER("));
const promptToUpload = lazyWebpack(filters.byCode("UPLOAD_FILE_LIMIT_ERROR"));
const UploadStore = lazyWebpack(filters.byProps(["getUploads"]));
const UploadStore = lazyWebpack(filters.byProps("getUploads"));
function loadImage(source: File | string) {
const isFile = source instanceof File;

@ -25,7 +25,7 @@ import { UserStore } from "../../../webpack/common";
import { PronounMapping } from "../types";
import { fetchPronouns, formatPronouns } from "../utils";
const styles: Record<string, string> = lazyWebpack(filters.byProps(["timestampInline"]));
const styles: Record<string, string> = lazyWebpack(filters.byProps("timestampInline"));
export default function PronounsChatComponent({ message }: { message: Message; }) {
// Don't bother fetching bot or system users

@ -29,7 +29,7 @@ const Engines = {
TinEye: "https://www.tineye.com/search?url="
};
const Menu = lazyWebpack(filters.byProps(["MenuItem"]));
const Menu = lazyWebpack(filters.byProps("MenuItem"));
export default definePlugin({

@ -53,9 +53,9 @@ interface Track {
name: string;
}
const Spotify = lazyWebpack(filters.byProps(["getPlayerState"]));
const MessageCreator = lazyWebpack(filters.byProps(["getSendMessageOptionsForReply", "sendMessage"]));
const PendingReplyStore = lazyWebpack(filters.byProps(["getPendingReply"]));
const Spotify = lazyWebpack(filters.byProps("getPlayerState"));
const MessageCreator = lazyWebpack(filters.byProps("getSendMessageOptionsForReply", "sendMessage"));
const PendingReplyStore = lazyWebpack(filters.byProps("getPendingReply"));
function sendMessage(channelId, message) {
message = {

@ -29,7 +29,7 @@ export default class Logger {
return ["%c %c %s ", "", `background: ${color}; color: black; font-weight: bold; border-radius: 5px;`, title];
}
constructor(public name: string, public color: string) { }
constructor(public name: string, public color: string = "white") { }
private _log(level: "log" | "error" | "warn" | "info" | "debug", levelColor: string, args: any[], customFmt = "") {
console[level](

@ -23,7 +23,7 @@ import type Stores from "discord-types/stores";
import { lazyWebpack } from "../utils/misc";
import { _resolveReady, filters, mapMangledModuleLazy, waitFor } from "./webpack";
export const Margins = lazyWebpack(filters.byProps(["marginTop20"]));
export const Margins = lazyWebpack(filters.byProps("marginTop20"));
export let FluxDispatcher: Other.FluxDispatcher;
export let React: typeof import("react");

@ -102,7 +102,7 @@ function patchPush() {
callback(exports.default);
}
for (const nested in exports) if (nested.length < 3) {
for (const nested in exports) if (nested.length <= 3) {
if (exports[nested] && filter(exports[nested])) {
subscriptions.delete(filter);
callback(exports[nested]);

@ -18,6 +18,7 @@
import type { WebpackInstance } from "discord-types/other";
import Logger from "../utils/logger";
import { proxyLazy } from "../utils/proxyLazy";
export let _resolveReady: () => void;
@ -33,11 +34,13 @@ export let cache: WebpackInstance["c"];
export type FilterFn = (mod: any) => boolean;
export const filters = {
byProps: (props: string[]): FilterFn =>
byProps: (...props: string[]): FilterFn =>
props.length === 1
? m => m[props[0]] !== void 0
: m => props.every(p => m[p] !== void 0),
byDisplayName: (deezNuts: string): FilterFn => m => m.default?.displayName === deezNuts,
byCode: (...code: string[]): FilterFn => m => {
if (typeof m !== "function") return false;
const s = Function.prototype.toString.call(m);
@ -48,6 +51,7 @@ export const filters = {
},
};
const logger = new Logger("Webpack");
export const subscriptions = new Map<FilterFn, CallbackFn>();
export const listeners = new Set<CallbackFn>();
@ -56,12 +60,12 @@ export type CallbackFn = (mod: any) => void;
export function _initWebpack(instance: typeof window.webpackChunkdiscord_app) {
if (cache !== void 0) throw "no.";
wreq = instance.push([[Symbol()], {}, r => r]);
wreq = instance.push([[Symbol("Vencord")], {}, r => r]);
cache = wreq.c;
instance.pop();
}
export function find(filter: FilterFn, getDefault = true) {
export function find(filter: FilterFn, getDefault = true, isWaitFor = false) {
if (typeof filter !== "function")
throw new Error("Invalid filter. Expected a function got " + typeof filter);
@ -77,7 +81,6 @@ export function find(filter: FilterFn, getDefault = true) {
if (mod.exports.default && filter(mod.exports.default))
return getDefault ? mod.exports.default : mod.exports;
// is 3 is the longest obfuscated export?
// the length check makes search about 20% faster
for (const nestedMod in mod.exports) if (nestedMod.length <= 3) {
const nested = mod.exports[nestedMod];
@ -85,11 +88,21 @@ export function find(filter: FilterFn, getDefault = true) {
}
}
if (!isWaitFor) {
const err = new Error("Didn't find module matching this filter");
if (IS_DEV) {
// Strict behaviour in DevBuilds to fail early and make sure the issue is found
throw err;
}
logger.warn(err);
}
return null;
}
export function findAll(filter: FilterFn, getDefault = true) {
if (typeof filter !== "function") throw new Error("Invalid filter. Expected a function got " + typeof filter);
if (typeof filter !== "function")
throw new Error("Invalid filter. Expected a function got " + typeof filter);
const ret = [] as any[];
for (const key in cache) {
@ -113,17 +126,17 @@ export function findAll(filter: FilterFn, getDefault = true) {
}
/**
* 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 Code snippet
* @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==")
* })
*/
* 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 Code snippet
* @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==")
* })
*/
export function mapMangledModule<S extends string>(code: string, mappers: Record<S, FilterFn>): Record<S, any> {
const exports = {} as Record<S, any>;
@ -143,26 +156,31 @@ export function mapMangledModule<S extends string>(code: string, mappers: Record
}
}
}
break;
return exports;
}
}
const err = new Error("Didn't find module matching this code:\n" + code);
if (IS_DEV)
throw err;
logger.warn(err);
return exports;
}
/**
* Same as {@link mapMangledModule} but lazy
*/
* Same as {@link mapMangledModule} but lazy
*/
export function mapMangledModuleLazy<S extends string>(code: string, mappers: Record<S, FilterFn>): Record<S, any> {
return proxyLazy(() => mapMangledModule(code, mappers));
}
export function findByProps(...props: string[]) {
return find(filters.byProps(props));
return find(filters.byProps(...props));
}
export function findAllByProps(...props: string[]) {
return findAll(filters.byProps(props));
return findAll(filters.byProps(...props));
}
export function findByDisplayName(deezNuts: string) {
@ -170,11 +188,14 @@ export function findByDisplayName(deezNuts: string) {
}
export function waitFor(filter: string | string[] | FilterFn, callback: CallbackFn) {
if (typeof filter === "string") filter = filters.byProps([filter]);
else if (Array.isArray(filter)) filter = filters.byProps(filter);
else if (typeof filter !== "function") throw new Error("filter must be a string, string[] or function, got " + typeof filter);
if (typeof filter === "string")
filter = filters.byProps(filter);
else if (Array.isArray(filter))
filter = filters.byProps(...filter);
else if (typeof filter !== "function")
throw new Error("filter must be a string, string[] or function, got " + typeof filter);
const existing = find(filter!);
const existing = find(filter!, true, true);
if (existing) return void callback(existing);
subscriptions.set(filter, callback);
@ -189,11 +210,11 @@ export function removeListener(callback: CallbackFn) {
}
/**
* Search modules by keyword. This searches the factory methods,
* meaning you can search all sorts of things, displayName, methodName, strings somewhere in the code, etc
* @param filters One or more strings or regexes
* @returns Mapping of found modules
*/
* Search modules by keyword. This searches the factory methods,
* meaning you can search all sorts of things, displayName, methodName, strings somewhere in the code, etc
* @param filters One or more strings or regexes
* @returns Mapping of found modules
*/
export function search(...filters: Array<string | RegExp>) {
const results = {} as Record<number, Function>;
const factories = wreq.m;
@ -212,13 +233,13 @@ export function search(...filters: Array<string | RegExp>) {
}
/**
* Extract a specific module by id into its own Source File. This has no effect on
* the code, it is only useful to be able to look at a specific module without having
* to view a massive file. extract then returns the extracted module so you can jump to it.
* As mentioned above, note that this extracted module is not actually used,
* so putting breakpoints or similar will have no effect.
* @param id The id of the module to extract
*/
* Extract a specific module by id into its own Source File. This has no effect on
* the code, it is only useful to be able to look at a specific module without having
* to view a massive file. extract then returns the extracted module so you can jump to it.
* As mentioned above, note that this extracted module is not actually used,
* so putting breakpoints or similar will have no effect.
* @param id The id of the module to extract
*/
export function extract(id: number) {
const mod = wreq.m[id] as Function;
if (!mod) return null;