Make Webpack finds not need to re-search
This commit is contained in:
parent
886071af4d
commit
4a89fadedd
|
@ -471,26 +471,19 @@ async function runtime(token: string) {
|
|||
}
|
||||
}
|
||||
|
||||
// Must evaluate the len outside of the loop, as the array will be modified by the find methods called inside of it
|
||||
// This will avoid an infinite loop
|
||||
const len = Vencord.Webpack.webpackSearchHistory.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
const [searchType, args] = Vencord.Webpack.webpackSearchHistory[i];
|
||||
const WEBPACK_SEARCH_HISTORY_WITH_FILTER_NAME_PROP = ["find", "findComponent", "waitFor"];
|
||||
|
||||
let method = searchType as string;
|
||||
if (searchType === "waitFor") method = "cacheFind";
|
||||
// eslint-disable-next-line prefer-const
|
||||
for (let [searchType, args] of Vencord.Webpack.webpackSearchHistory) {
|
||||
args = [...args];
|
||||
|
||||
try {
|
||||
let result: any;
|
||||
let result = null as any;
|
||||
|
||||
if (method === "webpackDependantLazy" || method === "webpackDependantLazyComponent") {
|
||||
if (searchType === "webpackDependantLazy" || searchType === "webpackDependantLazyComponent") {
|
||||
const [factory] = args;
|
||||
result = factory();
|
||||
|
||||
if (result != null && "$$vencordGetter" in result) {
|
||||
result = result.$$vencordGetter();
|
||||
}
|
||||
} else if (method === "extractAndLoadChunks") {
|
||||
} else if (searchType === "extractAndLoadChunks") {
|
||||
const [code, matcher] = args;
|
||||
|
||||
const module = Vencord.Webpack.findModuleFactory(...code);
|
||||
|
@ -498,14 +491,20 @@ async function runtime(token: string) {
|
|||
result = module.toString().match(Vencord.Util.canonicalizeMatch(matcher));
|
||||
}
|
||||
} else {
|
||||
result = Vencord.Webpack[method](...args);
|
||||
result = args.shift();
|
||||
|
||||
// If the result is our Proxy or ComponentWrapper, this means the search failed
|
||||
if (
|
||||
result != null &&
|
||||
(result[Vencord.Util.proxyInnerGet] != null || "$$vencordGetter" in result)
|
||||
) {
|
||||
result = undefined;
|
||||
if (result != null) {
|
||||
if (result.$$vencordCallbackCalled != null && !result.$$vencordCallbackCalled()) {
|
||||
result = null;
|
||||
}
|
||||
|
||||
if (result[Vencord.Util.proxyInnerGet] != null) {
|
||||
result = result[Vencord.Util.proxyInnerValue];
|
||||
}
|
||||
|
||||
if (result.$$vencordInner != null) {
|
||||
result = result.$$vencordInner();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -517,28 +516,27 @@ async function runtime(token: string) {
|
|||
|
||||
let filterName = "";
|
||||
let parsedArgs = args;
|
||||
if ("$$vencordProps" in args[0]) {
|
||||
if (
|
||||
searchType === "find" ||
|
||||
searchType === "findComponent" ||
|
||||
searchType === "waitFor"
|
||||
) {
|
||||
filterName = args[0].$$vencordProps[0];
|
||||
|
||||
if (args[0].$$vencordProps != null) {
|
||||
if (WEBPACK_SEARCH_HISTORY_WITH_FILTER_NAME_PROP.includes(searchType)) {
|
||||
filterName = args[0].$$vencordProps.shift();
|
||||
}
|
||||
|
||||
parsedArgs = args[0].$$vencordProps.slice(1);
|
||||
parsedArgs = args[0].$$vencordProps;
|
||||
}
|
||||
|
||||
// if parsedArgs is the same as args, it means vencordProps of the filter was not available (like in normal filter functions),
|
||||
// so log the filter function instead
|
||||
if (
|
||||
parsedArgs === args && searchType === "waitFor" ||
|
||||
parsedArgs === args && (searchType === "waitFor" ||
|
||||
searchType === "find" ||
|
||||
searchType === "findComponent" ||
|
||||
searchType === "webpackDependantLazy" ||
|
||||
searchType === "webpackDependantLazyComponent"
|
||||
searchType === "webpackDependantLazyComponent")
|
||||
) {
|
||||
logMessage += `(${parsedArgs[0].toString().slice(0, 147)}...)`;
|
||||
logMessage += `(${args[0].toString().slice(0, 147)}...)`;
|
||||
} else if (searchType === "extractAndLoadChunks") {
|
||||
logMessage += `([${parsedArgs[0].map((arg: any) => `"${arg}"`).join(", ")}], ${parsedArgs[1].toString()})`;
|
||||
logMessage += `([${args[0].map((arg: any) => `"${arg}"`).join(", ")}], ${args[1].toString()})`;
|
||||
} else {
|
||||
logMessage += `(${filterName.length ? `${filterName}(` : ""}${parsedArgs.map(arg => `"${arg}"`).join(", ")})${filterName.length ? ")" : ""}`;
|
||||
}
|
||||
|
|
|
@ -4,17 +4,18 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export function makeLazy<T>(factory: () => T, attempts = 5): () => T {
|
||||
export function makeLazy<T>(factory: () => T, attempts = 5, { isIndirect = false }: { isIndirect?: boolean; } = {}): () => T {
|
||||
let tries = 0;
|
||||
let cache: T;
|
||||
|
||||
const getter = () => {
|
||||
if (!cache && attempts > tries) {
|
||||
cache = factory();
|
||||
if (!cache && attempts === ++tries) {
|
||||
if (!cache && attempts === ++tries && !isIndirect) {
|
||||
console.error(`Lazy factory failed:\n\n${factory}`);
|
||||
}
|
||||
}
|
||||
|
||||
return cache;
|
||||
};
|
||||
|
||||
|
@ -48,8 +49,8 @@ const handler: ProxyHandler<any> = {
|
|||
}
|
||||
};
|
||||
|
||||
const proxyLazyGet = Symbol.for("vencord.lazy.get");
|
||||
const proxyLazyCache = Symbol.for("vencord.lazy.cached");
|
||||
export const proxyLazyGet = Symbol.for("vencord.lazy.get");
|
||||
export const proxyLazyCache = Symbol.for("vencord.lazy.cached");
|
||||
|
||||
/**
|
||||
* Wraps the result of factory in a Proxy you can consume as if it wasn't lazy.
|
||||
|
@ -59,7 +60,7 @@ const proxyLazyCache = Symbol.for("vencord.lazy.cached");
|
|||
* @returns Result of factory function
|
||||
*/
|
||||
export function proxyLazy<T = any>(factory: () => T, attempts = 5, isChild = false): T {
|
||||
const get = makeLazy(factory, attempts) as any;
|
||||
const get = makeLazy(factory, attempts, { isIndirect: true }) as any;
|
||||
|
||||
let isSameTick = true;
|
||||
if (!isChild) setTimeout(() => isSameTick = false, 0);
|
||||
|
@ -84,6 +85,9 @@ export function proxyLazy<T = any>(factory: () => T, attempts = 5, isChild = fal
|
|||
return new Proxy(proxyDummy, {
|
||||
...handler,
|
||||
get(target, p, receiver) {
|
||||
if (p === proxyLazyGet) return target[proxyLazyGet];
|
||||
if (p === proxyLazyCache) return target[proxyLazyCache];
|
||||
|
||||
// If we're still in the same tick, it means the lazy was immediately used.
|
||||
// thus, we lazy proxy the get access to make things like destructuring work as expected
|
||||
// meow here will also be a lazy
|
||||
|
|
|
@ -4,12 +4,8 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { ComponentType } from "react";
|
||||
|
||||
import { makeLazy } from "./lazy";
|
||||
|
||||
export const NoopComponent = () => null;
|
||||
|
||||
/**
|
||||
* A lazy component. The factory method is called on first render.
|
||||
* @param factory Function returning a component
|
||||
|
@ -17,25 +13,31 @@ export const NoopComponent = () => null;
|
|||
* @returns Result of factory function
|
||||
*/
|
||||
export function LazyComponent<T extends object = any>(factory: () => React.ComponentType<T>, attempts = 5) {
|
||||
const get = makeLazy(factory, attempts);
|
||||
const get = makeLazy(factory, attempts, { isIndirect: true });
|
||||
|
||||
let InnerComponent = null as React.ComponentType<T> | null;
|
||||
|
||||
let lazyFailedLogged = false;
|
||||
const LazyComponent = (props: T) => {
|
||||
let Component = (() => {
|
||||
console.error(`LazyComponent factory failed:\n\n${factory}`);
|
||||
|
||||
return null;
|
||||
}) as React.ComponentType<T>;
|
||||
|
||||
// @ts-ignore
|
||||
if (!get.$$vencordLazyFailed()) {
|
||||
const result = get();
|
||||
if (result != null) Component = result;
|
||||
const ResultComponent = get();
|
||||
if (ResultComponent != null) {
|
||||
InnerComponent = ResultComponent;
|
||||
}
|
||||
}
|
||||
|
||||
return <Component {...props} />;
|
||||
if (InnerComponent === null && !lazyFailedLogged) {
|
||||
// @ts-ignore
|
||||
if (get.$$vencordLazyFailed()) {
|
||||
lazyFailedLogged = true;
|
||||
}
|
||||
|
||||
console.error(`LazyComponent factory failed:\n\n${factory}`);
|
||||
}
|
||||
|
||||
return InnerComponent && <InnerComponent {...props} />;
|
||||
};
|
||||
|
||||
LazyComponent.$$vencordGetter = get;
|
||||
|
||||
return LazyComponent as ComponentType<T>;
|
||||
return LazyComponent as React.ComponentType<T>;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ import { checkIntersecting } from "./misc";
|
|||
|
||||
export * from "./lazyReact";
|
||||
|
||||
export const NoopComponent = () => null;
|
||||
|
||||
/**
|
||||
* Check if an element is on screen
|
||||
* @param intersectOnly If `true`, will only update the state when the element comes into view
|
||||
|
|
|
@ -133,7 +133,22 @@ export function waitFor(filter: FilterFn, callback: ModCallbackFn, { isIndirect
|
|||
if (typeof callback !== "function")
|
||||
throw new Error("Invalid callback. Expected a function got " + typeof callback);
|
||||
|
||||
if (IS_DEV && !isIndirect) webpackSearchHistory.push(["waitFor", [filter]]);
|
||||
if (IS_DEV && !isIndirect) {
|
||||
const originalCallback = callback;
|
||||
|
||||
let callbackCalled = false;
|
||||
callback = function () {
|
||||
callbackCalled = true;
|
||||
|
||||
// @ts-ignore
|
||||
originalCallback(...arguments);
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
callback.$$vencordCallbackCalled = () => callbackCalled;
|
||||
|
||||
webpackSearchHistory.push(["waitFor", [callback, filter]]);
|
||||
}
|
||||
|
||||
if (cache != null) {
|
||||
const existing = cacheFind(filter);
|
||||
|
@ -165,11 +180,13 @@ export function find<T = any>(filter: FilterFn, callback: (mod: any) => any = m
|
|||
if (typeof callback !== "function")
|
||||
throw new Error("Invalid callback. Expected a function got " + typeof callback);
|
||||
|
||||
if (IS_DEV && !isIndirect) webpackSearchHistory.push(["find", [filter]]);
|
||||
|
||||
const [proxy, setInnerValue] = proxyInner<T>(`Webpack find matched no module. Filter: ${printFilter(filter)}`, "Webpack find with proxy called on a primitive value. This can happen if you try to destructure a primitive in the top level definition of the find.");
|
||||
waitFor(filter, mod => setInnerValue(callback(mod)), { isIndirect: true });
|
||||
|
||||
if (IS_DEV && !isIndirect) {
|
||||
webpackSearchHistory.push(["find", [proxy, filter]]);
|
||||
}
|
||||
|
||||
if (proxy[proxyInnerValue] != null) return proxy[proxyInnerValue] as T;
|
||||
|
||||
return proxy;
|
||||
|
@ -187,9 +204,7 @@ export function findComponent<T extends object = any>(filter: FilterFn, parse: (
|
|||
if (typeof parse !== "function")
|
||||
throw new Error("Invalid component parse. Expected a function got " + typeof parse);
|
||||
|
||||
if (IS_DEV && !isIndirect) webpackSearchHistory.push(["findComponent", [filter]]);
|
||||
|
||||
let InnerComponent = null as null | React.ComponentType<T>;
|
||||
let InnerComponent = null as React.ComponentType<T> | null;
|
||||
|
||||
let findFailedLogged = false;
|
||||
const WrapperComponent = (props: T) => {
|
||||
|
@ -201,14 +216,20 @@ export function findComponent<T extends object = any>(filter: FilterFn, parse: (
|
|||
return InnerComponent && <InnerComponent {...props} />;
|
||||
};
|
||||
|
||||
WrapperComponent.$$vencordGetter = () => InnerComponent;
|
||||
|
||||
waitFor(filter, (v: any) => {
|
||||
const parsedComponent = parse(v);
|
||||
InnerComponent = parsedComponent;
|
||||
Object.assign(InnerComponent, parsedComponent);
|
||||
}, { isIndirect: true });
|
||||
|
||||
if (IS_DEV) {
|
||||
WrapperComponent.$$vencordInner = () => InnerComponent;
|
||||
|
||||
if (!isIndirect) {
|
||||
webpackSearchHistory.push(["findComponent", [WrapperComponent, filter]]);
|
||||
}
|
||||
}
|
||||
|
||||
if (InnerComponent !== null) return InnerComponent;
|
||||
|
||||
return WrapperComponent;
|
||||
|
@ -230,9 +251,7 @@ export function findExportedComponent<T extends object = any>(...props: string[]
|
|||
|
||||
const filter = filters.byProps(...newProps);
|
||||
|
||||
if (IS_DEV) webpackSearchHistory.push(["findExportedComponent", props]);
|
||||
|
||||
let InnerComponent = null as null | React.ComponentType<T>;
|
||||
let InnerComponent = null as React.ComponentType<T> | null;
|
||||
|
||||
let findFailedLogged = false;
|
||||
const WrapperComponent = (props: T) => {
|
||||
|
@ -244,7 +263,6 @@ export function findExportedComponent<T extends object = any>(...props: string[]
|
|||
return InnerComponent && <InnerComponent {...props} />;
|
||||
};
|
||||
|
||||
WrapperComponent.$$vencordGetter = () => InnerComponent;
|
||||
|
||||
waitFor(filter, (v: any) => {
|
||||
const parsedComponent = parse(v[newProps[0]]);
|
||||
|
@ -252,6 +270,11 @@ export function findExportedComponent<T extends object = any>(...props: string[]
|
|||
Object.assign(InnerComponent, parsedComponent);
|
||||
}, { isIndirect: true });
|
||||
|
||||
if (IS_DEV) {
|
||||
WrapperComponent.$$vencordInner = () => InnerComponent;
|
||||
webpackSearchHistory.push(["findExportedComponent", [WrapperComponent, ...props]]);
|
||||
}
|
||||
|
||||
if (InnerComponent !== null) return InnerComponent;
|
||||
|
||||
return WrapperComponent as React.ComponentType<T>;
|
||||
|
@ -271,9 +294,13 @@ export function findComponentByCode<T extends object = any>(...code: string[] |
|
|||
const parse = (typeof code.at(-1) === "function" ? code.pop() : m => m) as (component: any) => React.ComponentType<T>;
|
||||
const newCode = code as string[];
|
||||
|
||||
if (IS_DEV) webpackSearchHistory.push(["findComponentByCode", code]);
|
||||
const ComponentResult = findComponent<T>(filters.componentByCode(...newCode), parse, { isIndirect: true });
|
||||
|
||||
return findComponent<T>(filters.componentByCode(...newCode), parse, { isIndirect: true });
|
||||
if (IS_DEV) {
|
||||
webpackSearchHistory.push(["findComponentByCode", [ComponentResult, ...code]]);
|
||||
}
|
||||
|
||||
return ComponentResult;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -282,9 +309,13 @@ export function findComponentByCode<T extends object = any>(...code: string[] |
|
|||
* @param props A list of props to search the exports for
|
||||
*/
|
||||
export function findByProps<T = any>(...props: string[]) {
|
||||
if (IS_DEV) webpackSearchHistory.push(["findByProps", props]);
|
||||
const result = find<T>(filters.byProps(...props), m => m, { isIndirect: true });
|
||||
|
||||
return find<T>(filters.byProps(...props), m => m, { isIndirect: true });
|
||||
if (IS_DEV) {
|
||||
webpackSearchHistory.push(["findByProps", [result, ...props]]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -293,9 +324,13 @@ export function findByProps<T = any>(...props: string[]) {
|
|||
* @param code A list of code to search each export for
|
||||
*/
|
||||
export function findByCode<T = any>(...code: string[]) {
|
||||
if (IS_DEV) webpackSearchHistory.push(["findByCode", code]);
|
||||
const result = find<T>(filters.byCode(...code), m => m, { isIndirect: true });
|
||||
|
||||
return find<T>(filters.byCode(...code), m => m, { isIndirect: true });
|
||||
if (IS_DEV) {
|
||||
webpackSearchHistory.push(["findByCode", [result, ...code]]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -304,9 +339,13 @@ export function findByCode<T = any>(...code: string[]) {
|
|||
* @param name The store name
|
||||
*/
|
||||
export function findStore<T = any>(name: string) {
|
||||
if (IS_DEV) webpackSearchHistory.push(["findStore", [name]]);
|
||||
const result = find<T>(filters.byStoreName(name), m => m, { isIndirect: true });
|
||||
|
||||
return find<T>(filters.byStoreName(name), m => m, { isIndirect: true });
|
||||
if (IS_DEV) {
|
||||
webpackSearchHistory.push(["findStore", [result, name]]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue