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

View file

@ -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();

View file

@ -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);

View file

@ -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 {

View file

@ -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"));

View file

@ -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",

View file

@ -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[] = [];

View file

@ -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[];

View file

@ -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;

View file

@ -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;

View 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

View file

@ -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({

View file

@ -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 = {

View file

@ -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](

View file

@ -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");

View file

@ -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]);

View file

@ -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;