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 { popNotice, showNotice } from "./api/Notices";
import { PlainSettings, Settings } from "./api/settings"; import { PlainSettings, Settings } from "./api/settings";
import { startAllPlugins } from "./plugins"; import { patches, PMLogger, startAllPlugins } from "./plugins";
export { PlainSettings, Settings }; export { PlainSettings, Settings };
@ -61,6 +61,19 @@ async function init() {
UpdateLogger.error("Failed to check for updates", err); 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(); init();

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

@ -38,7 +38,7 @@ import {
} from "./components"; } from "./components";
const UserSummaryItem = lazyWebpack(filters.byCode("defaultRenderUser", "showDefaultAvatarsForNullUsers")); 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; const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any;
interface PluginModalProps extends ModalProps { interface PluginModalProps extends ModalProps {

@ -36,7 +36,7 @@ import * as styles from "./styles";
const logger = new Logger("PluginSettings", "#a6d189"); const logger = new Logger("PluginSettings", "#a6d189");
const Select = lazyWebpack(filters.byCode("optionClassName", "popoutPosition", "autoFocus", "maxVisibleItems")); 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 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")); 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 { filters } from "../webpack";
import { Forms, React } from "../webpack/common"; import { Forms, React } from "../webpack/common";
const KbdStyles = lazyWebpack(filters.byProps(["key", "removeBuildOverride"])); const KbdStyles = lazyWebpack(filters.byProps("key", "removeBuildOverride"));
export default definePlugin({ export default definePlugin({
name: "Experiments", name: "Experiments",

@ -28,7 +28,7 @@ interface MatchAndReplace {
} }
/** Used to re-render the Registered Games tab to update how our button looks like */ /** 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[] = []; let ignoredActivitiesCache: string[] = [];

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

@ -24,7 +24,7 @@ import definePlugin from "../utils/types";
import { filters } from "../webpack"; import { filters } from "../webpack";
import { ChannelStore, FluxDispatcher as Dispatcher, SelectedChannelStore, UserStore } from "../webpack/common"; 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 const isMac = navigator.platform.includes("Mac"); // bruh
let replyIdx = -1; let replyIdx = -1;

@ -41,7 +41,7 @@ const getFrames = makeLazy(() => Promise.all(
const fetchUser = lazyWebpack(filters.byCode(".USER(")); const fetchUser = lazyWebpack(filters.byCode(".USER("));
const promptToUpload = lazyWebpack(filters.byCode("UPLOAD_FILE_LIMIT_ERROR")); 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) { function loadImage(source: File | string) {
const isFile = source instanceof File; const isFile = source instanceof File;

@ -25,7 +25,7 @@ import { UserStore } from "../../../webpack/common";
import { PronounMapping } from "../types"; import { PronounMapping } from "../types";
import { fetchPronouns, formatPronouns } from "../utils"; 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; }) { export default function PronounsChatComponent({ message }: { message: Message; }) {
// Don't bother fetching bot or system users // Don't bother fetching bot or system users

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

@ -53,9 +53,9 @@ interface Track {
name: string; name: string;
} }
const Spotify = lazyWebpack(filters.byProps(["getPlayerState"])); const Spotify = lazyWebpack(filters.byProps("getPlayerState"));
const MessageCreator = lazyWebpack(filters.byProps(["getSendMessageOptionsForReply", "sendMessage"])); const MessageCreator = lazyWebpack(filters.byProps("getSendMessageOptionsForReply", "sendMessage"));
const PendingReplyStore = lazyWebpack(filters.byProps(["getPendingReply"])); const PendingReplyStore = lazyWebpack(filters.byProps("getPendingReply"));
function sendMessage(channelId, message) { function sendMessage(channelId, message) {
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]; 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 = "") { private _log(level: "log" | "error" | "warn" | "info" | "debug", levelColor: string, args: any[], customFmt = "") {
console[level]( console[level](

@ -23,7 +23,7 @@ import type Stores from "discord-types/stores";
import { lazyWebpack } from "../utils/misc"; import { lazyWebpack } from "../utils/misc";
import { _resolveReady, filters, mapMangledModuleLazy, waitFor } from "./webpack"; 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 FluxDispatcher: Other.FluxDispatcher;
export let React: typeof import("react"); export let React: typeof import("react");

@ -102,7 +102,7 @@ function patchPush() {
callback(exports.default); 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])) { if (exports[nested] && filter(exports[nested])) {
subscriptions.delete(filter); subscriptions.delete(filter);
callback(exports[nested]); callback(exports[nested]);

@ -18,6 +18,7 @@
import type { WebpackInstance } from "discord-types/other"; import type { WebpackInstance } from "discord-types/other";
import Logger from "../utils/logger";
import { proxyLazy } from "../utils/proxyLazy"; import { proxyLazy } from "../utils/proxyLazy";
export let _resolveReady: () => void; export let _resolveReady: () => void;
@ -33,11 +34,13 @@ export let cache: WebpackInstance["c"];
export type FilterFn = (mod: any) => boolean; export type FilterFn = (mod: any) => boolean;
export const filters = { export const filters = {
byProps: (props: string[]): FilterFn => byProps: (...props: string[]): FilterFn =>
props.length === 1 props.length === 1
? m => m[props[0]] !== void 0 ? m => m[props[0]] !== void 0
: m => props.every(p => m[p] !== void 0), : m => props.every(p => m[p] !== void 0),
byDisplayName: (deezNuts: string): FilterFn => m => m.default?.displayName === deezNuts, byDisplayName: (deezNuts: string): FilterFn => m => m.default?.displayName === deezNuts,
byCode: (...code: string[]): FilterFn => m => { byCode: (...code: string[]): FilterFn => m => {
if (typeof m !== "function") return false; if (typeof m !== "function") return false;
const s = Function.prototype.toString.call(m); 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 subscriptions = new Map<FilterFn, CallbackFn>();
export const listeners = new Set<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) { export function _initWebpack(instance: typeof window.webpackChunkdiscord_app) {
if (cache !== void 0) throw "no."; if (cache !== void 0) throw "no.";
wreq = instance.push([[Symbol()], {}, r => r]); wreq = instance.push([[Symbol("Vencord")], {}, r => r]);
cache = wreq.c; cache = wreq.c;
instance.pop(); instance.pop();
} }
export function find(filter: FilterFn, getDefault = true) { export function find(filter: FilterFn, getDefault = true, isWaitFor = false) {
if (typeof filter !== "function") if (typeof filter !== "function")
throw new Error("Invalid filter. Expected a function got " + typeof filter); 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)) if (mod.exports.default && filter(mod.exports.default))
return getDefault ? mod.exports.default : mod.exports; return getDefault ? mod.exports.default : mod.exports;
// is 3 is the longest obfuscated export?
// the length check makes search about 20% faster // the length check makes search about 20% faster
for (const nestedMod in mod.exports) if (nestedMod.length <= 3) { for (const nestedMod in mod.exports) if (nestedMod.length <= 3) {
const nested = mod.exports[nestedMod]; 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; return null;
} }
export function findAll(filter: FilterFn, getDefault = true) { 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[]; const ret = [] as any[];
for (const key in cache) { for (const key in cache) {
@ -143,10 +156,15 @@ 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; return exports;
} }
@ -158,11 +176,11 @@ export function mapMangledModuleLazy<S extends string>(code: string, mappers: Re
} }
export function findByProps(...props: string[]) { export function findByProps(...props: string[]) {
return find(filters.byProps(props)); return find(filters.byProps(...props));
} }
export function findAllByProps(...props: string[]) { export function findAllByProps(...props: string[]) {
return findAll(filters.byProps(props)); return findAll(filters.byProps(...props));
} }
export function findByDisplayName(deezNuts: string) { export function findByDisplayName(deezNuts: string) {
@ -170,11 +188,14 @@ export function findByDisplayName(deezNuts: string) {
} }
export function waitFor(filter: string | string[] | FilterFn, callback: CallbackFn) { export function waitFor(filter: string | string[] | FilterFn, callback: CallbackFn) {
if (typeof filter === "string") filter = filters.byProps([filter]); if (typeof filter === "string")
else if (Array.isArray(filter)) filter = filters.byProps(filter); filter = filters.byProps(filter);
else if (typeof filter !== "function") throw new Error("filter must be a string, string[] or function, got " + typeof 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); if (existing) return void callback(existing);
subscriptions.set(filter, callback); subscriptions.set(filter, callback);