fix(metro): unlazify react components when needed
This commit is contained in:
parent
dcb0408e31
commit
4057995a86
8 changed files with 78 additions and 49 deletions
|
|
@ -95,6 +95,9 @@ try {
|
|||
version: `"${isRelease ? commitHash : timeString}"`
|
||||
}
|
||||
}
|
||||
},
|
||||
react: {
|
||||
pragma: "__bunny_createElement"
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,9 +1,17 @@
|
|||
import { getFactoryOfProxy } from "@lib/utils/lazy";
|
||||
import { version } from "bunny-build";
|
||||
import { instead } from "spitroast";
|
||||
|
||||
// @ts-ignore - shut up fr
|
||||
globalThis.window = globalThis;
|
||||
|
||||
// @ts-ignore && TODO: Move this to a more sensible place
|
||||
globalThis.__bunny_createElement = function createElement(type, props, ...children) {
|
||||
const factory = getFactoryOfProxy(type);
|
||||
if (factory) type = factory();
|
||||
return React.createElement(type, props, ...children);
|
||||
};
|
||||
|
||||
async function initializeBunny() {
|
||||
try {
|
||||
// Make 'freeze' and 'seal' do nothing
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ import { ThemeManager } from "@lib/api/native/modules";
|
|||
import { after, before, instead } from "@lib/api/patcher";
|
||||
import { awaitSyncWrapper, createFileBackend, createMMKVBackend, createStorage, wrapSync } from "@lib/api/storage";
|
||||
import { findInReactTree, safeFetch } from "@lib/utils";
|
||||
import { lazyDestructure } from "@lib/utils/lazy";
|
||||
import { lazyDestructure, proxyLazy } from "@lib/utils/lazy";
|
||||
import { Author } from "@lib/utils/types";
|
||||
import { chroma } from "@metro/common";
|
||||
import { byMutableProp } from "@metro/filters";
|
||||
import { findExports } from "@metro/finders";
|
||||
import { createFindProxy } from "@metro/proxy";
|
||||
import { findByNameProxy, findByProps, findByPropsProxy, findByStoreNameProxy } from "@metro/utils";
|
||||
import { ImageBackground, Platform, processColor } from "react-native";
|
||||
|
||||
|
|
@ -44,10 +44,18 @@ export interface Theme {
|
|||
// Somehow, this is late enough, though?
|
||||
export const color = findByPropsProxy("SemanticColor");
|
||||
|
||||
const mmkvStorage = proxyLazy(() => {
|
||||
const newModule = findByProps("impl");
|
||||
if (typeof newModule?.impl === "object") return newModule.impl;
|
||||
return findByProps("storage");
|
||||
});
|
||||
|
||||
const appearanceManager = findByPropsProxy("updateTheme");
|
||||
const mmkvStorage = findByPropsProxy("storage")?.parseResolve ? findByPropsProxy("storage") : findByPropsProxy("impl").impl;
|
||||
const ThemeStore = findByStoreNameProxy("ThemeStore");
|
||||
const formDividerModule = findByPropsProxy("DIVIDER_COLORS");
|
||||
const MessagesWrapperConnected = findByNameProxy("MessagesWrapperConnected", false);
|
||||
const { MessagesWrapper } = lazyDestructure(() => findByProps("MessagesWrapper"));
|
||||
const isThemeModule = createFindProxy(byMutableProp("isThemeDark"));
|
||||
|
||||
export const themes = wrapSync(createStorage<Record<string, Theme>>(createMMKVBackend("VENDETTA_THEMES")));
|
||||
|
||||
|
|
@ -76,9 +84,6 @@ async function writeTheme(theme: Theme | {}) {
|
|||
* @internal
|
||||
*/
|
||||
export function patchChatBackground() {
|
||||
const MessagesWrapperConnected = findByNameProxy("MessagesWrapperConnected", false);
|
||||
const { MessagesWrapper } = lazyDestructure(() => findByProps("MessagesWrapper"));
|
||||
|
||||
const patches = [
|
||||
after("default", MessagesWrapperConnected, (_, ret) => enabled ? React.createElement(ImageBackground, {
|
||||
style: { flex: 1, height: "100%" },
|
||||
|
|
@ -250,7 +255,6 @@ function isDiscordTheme(name: string) {
|
|||
}
|
||||
|
||||
function patchColor() {
|
||||
const isThemeModule = findExports(byMutableProp("isThemeDark"));
|
||||
const callback = ([theme]: any[]) => theme === vdKey ? [vdThemeFallback] : void 0;
|
||||
|
||||
Object.keys(color.RawColor).forEach(k => {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { lazyDestructure } from "@lib/utils/lazy";
|
||||
import { findByPropsProxy } from "@metro/utils";
|
||||
|
||||
export const Forms = findByPropsProxy("Form", "FormSection");
|
||||
|
|
@ -29,4 +30,4 @@ export const {
|
|||
FormTernaryCheckBox,
|
||||
FormText,
|
||||
FormTitle
|
||||
} = Forms;
|
||||
} = lazyDestructure(() => Forms);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { findExports } from "@metro/finders";
|
||||
import { lazyDestructure, proxyLazy } from "@lib/utils/lazy";
|
||||
import { findExports } from "@metro";
|
||||
import { createFilterDefinition, findByPropsProxy } from "@metro/utils";
|
||||
import { RedesignModuleKeys } from "@ui/types";
|
||||
import { useEffect } from "react";
|
||||
|
|
@ -9,7 +10,7 @@ const bySingularProp = createFilterDefinition<[string]>(
|
|||
prop => `bunny.ui.bySingularProp(${prop})`
|
||||
);
|
||||
|
||||
const findSingular = (prop: string) => findExports(bySingularProp(prop))?.[prop];
|
||||
const findSingular = (prop: string) => proxyLazy(() => findExports(bySingularProp(prop))?.[prop]);
|
||||
|
||||
export const Redesign = findByPropsProxy("TableRow") as Record<RedesignModuleKeys, any>;
|
||||
|
||||
|
|
@ -117,7 +118,7 @@ export const {
|
|||
useStackNavigation,
|
||||
useTabNavigation,
|
||||
useTooltip
|
||||
} = Redesign;
|
||||
} = lazyDestructure(() => Redesign);
|
||||
|
||||
export const {
|
||||
AccessibilityAnnouncer,
|
||||
|
|
@ -166,4 +167,4 @@ export const {
|
|||
useLegacyClassComponentStyles,
|
||||
useThemeContext,
|
||||
useToken
|
||||
} = CompatfulRedesign;
|
||||
} = lazyDestructure(() => CompatfulRedesign);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
const factorySymbol = Symbol("lazyFactory");
|
||||
const cacheSymbol = Symbol("lazyCache");
|
||||
const factorySymbol = Symbol.for("bunny.lazy.factory");
|
||||
const cacheSymbol = Symbol.for("bunny.lazy.cache");
|
||||
|
||||
const unconfigurable = ["arguments", "caller", "prototype"];
|
||||
const isUnconfigurable = (key: PropertyKey) => typeof key === "string" && unconfigurable.includes(key);
|
||||
|
||||
// eslint-disable-next-line func-call-spacing
|
||||
const proxyToFactoryMap = new WeakMap<any, () => any>();
|
||||
|
||||
const lazyHandler: ProxyHandler<any> = {
|
||||
ownKeys: target => {
|
||||
const cacheKeys = Reflect.ownKeys(target[factorySymbol]());
|
||||
|
|
@ -41,7 +44,9 @@ export function proxyLazy<T>(factory: () => T, asFunction = true): T {
|
|||
return dummy[cacheSymbol] ??= factory();
|
||||
};
|
||||
|
||||
return new Proxy(dummy, lazyHandler) as any;
|
||||
const proxy = new Proxy(dummy, lazyHandler) as T;
|
||||
proxyToFactoryMap.set(proxy, dummy[factorySymbol]);
|
||||
return proxy;
|
||||
}
|
||||
|
||||
export function lazyDestructure<T extends Record<PropertyKey, unknown>>(factory: () => T, asFunction = false): T {
|
||||
|
|
@ -62,3 +67,7 @@ export function lazyDestructure<T extends Record<PropertyKey, unknown>>(factory:
|
|||
}
|
||||
}) as T;
|
||||
}
|
||||
|
||||
export function getFactoryOfProxy<T>(obj: T): (() => T) | void {
|
||||
return proxyToFactoryMap.get(obj) as (() => T) | void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,8 +141,11 @@ function onModuleRequire(moduleExports: any, id: Metro.ModuleID) {
|
|||
});
|
||||
}
|
||||
|
||||
moduleSubscriptions.get(Number(id))?.forEach(s => s())
|
||||
&& moduleSubscriptions.delete(Number(id));
|
||||
const subs = moduleSubscriptions.get(Number(id));
|
||||
if (subs) {
|
||||
subs.forEach(s => s());
|
||||
moduleSubscriptions.delete(Number(id));
|
||||
}
|
||||
}
|
||||
|
||||
export function getImportingModuleId() {
|
||||
|
|
@ -174,9 +177,7 @@ export function requireModule(id: Metro.ModuleID) {
|
|||
|
||||
let moduleExports;
|
||||
try {
|
||||
const forced = !metroModules[id]?.isInitialized;
|
||||
moduleExports = metroRequire(id);
|
||||
forced && console.log("Force inited", id, moduleExports);
|
||||
} catch {
|
||||
blacklistModule(String(id));
|
||||
moduleExports = undefined;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { getPolyfillModuleCacher } from "@metro/caches";
|
||||
import { byProps } from "@metro/filters";
|
||||
import { LiteralUnion } from "type-fest";
|
||||
|
||||
const redesignProps = new Set([
|
||||
|
|
@ -99,41 +100,42 @@ const redesignProps = new Set([
|
|||
|
||||
type Keys = LiteralUnion<typeof redesignProps extends Set<infer U> ? U : string, string>;
|
||||
|
||||
const redesignPropSource = {} as Record<Keys, any>;
|
||||
const redesignModule = {} as Record<Keys, any>;
|
||||
|
||||
const cacher = getPolyfillModuleCacher("redesign_module");
|
||||
|
||||
for (const [id, moduleExports] of cacher.getModules()) {
|
||||
polyfillRedesignModule(moduleExports, Number(id));
|
||||
}
|
||||
const _cache = {} as Record<Keys, any>;
|
||||
|
||||
function polyfillRedesignModule(moduleExports: any, id: number) {
|
||||
const propMap = new Map<string, string | null>();
|
||||
export default new Proxy({}, {
|
||||
get(_, p) {
|
||||
if (typeof p !== "string" || !redesignProps.has(p as any)) return;
|
||||
if (_cache[p]) return _cache[p];
|
||||
|
||||
for (const prop of redesignProps) {
|
||||
if (moduleExports?.[prop]) {
|
||||
cacher.cacheId(id);
|
||||
propMap.set(prop, null);
|
||||
} else if (moduleExports?.default?.[prop]) {
|
||||
cacher.cacheId(id);
|
||||
propMap.set(prop, "default");
|
||||
}
|
||||
}
|
||||
const prop = p as Keys;
|
||||
const filter = byProps(prop);
|
||||
const candidates: any[] = [];
|
||||
|
||||
for (const [prop, defaultKey] of propMap) {
|
||||
const exportsForProp = defaultKey ? moduleExports[defaultKey] : moduleExports;
|
||||
|
||||
if (redesignModule[prop]) {
|
||||
if (Object.keys(exportsForProp).length < Object.keys(redesignPropSource[prop]).length) {
|
||||
redesignModule[prop] = exportsForProp[prop];
|
||||
redesignPropSource[prop] = exportsForProp;
|
||||
for (const [id, moduleExports] of cacher.getModules()) {
|
||||
if (filter(moduleExports, id, false)) {
|
||||
cacher.cacheId(id);
|
||||
candidates.push(moduleExports);
|
||||
} else if (
|
||||
moduleExports.__esModule
|
||||
&& moduleExports.default
|
||||
&& filter(moduleExports.default, id, false)
|
||||
) {
|
||||
cacher.cacheId(id);
|
||||
candidates.push(moduleExports.default);
|
||||
}
|
||||
} else {
|
||||
redesignModule[prop] = exportsForProp[prop];
|
||||
redesignPropSource[prop] = exportsForProp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default redesignModule;
|
||||
if (candidates.length === 0) return undefined;
|
||||
|
||||
const bestCandidate = candidates.reduce(
|
||||
(c1, c2) =>
|
||||
(Object.keys(c2).length < Object.keys(c1).length)
|
||||
? c2
|
||||
: c1
|
||||
);
|
||||
|
||||
return _cache[prop] = bestCandidate[prop];
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue