import { lazy, type ComponentType, type FocusEventHandler, type LazyExoticComponent, type PointerEventHandler, type TouchEventHandler, } from 'react'; export interface ComponentModule> { default: ComponentType; } export type PreloadableComponent> = LazyExoticComponent< ComponentType > & { preload: () => Promise>; read: () => ComponentModule | undefined; clearPreload: () => void; }; export interface PreloadableLike { preload: () => Promise; } export interface PreloadAllOptions { settle?: boolean; } export interface IntentPreloadOptions { once?: boolean; } export interface IntentPreloadHandlers { onPointerEnter: PointerEventHandler; onFocus: FocusEventHandler; onTouchStart: TouchEventHandler; } export function lazyWithPreload>( factory: () => Promise>, ): PreloadableComponent { let promise: Promise> | undefined; let resolvedModule: ComponentModule | undefined; let thrownError: unknown; const load = () => { if (!promise) { promise = factory().then( (module) => { resolvedModule = module; thrownError = undefined; return module; }, (error: unknown) => { thrownError = error; promise = undefined; throw error; }, ); } return promise; }; const LazyComponent = lazy(load) as PreloadableComponent; LazyComponent.preload = load; LazyComponent.read = () => { if (thrownError) { throw thrownError; } return resolvedModule; }; LazyComponent.clearPreload = () => { promise = undefined; resolvedModule = undefined; thrownError = undefined; }; return LazyComponent; } export async function preloadAll( preloadables: Array Promise)>, options: PreloadAllOptions = {}, ): Promise[]> { const promises = preloadables.map((item) => typeof item === 'function' && !('preload' in item) ? item() : (item as PreloadableLike).preload(), ); if (options.settle) { return Promise.allSettled(promises); } return Promise.all(promises); } export function createIntentPreloader( preload: () => Promise, options: IntentPreloadOptions = {}, ): IntentPreloadHandlers { const once = options.once ?? true; let hasStarted = false; const start = () => { if (once && hasStarted) { return; } hasStarted = true; void preload(); }; return { onPointerEnter: start, onFocus: start, onTouchStart: start, }; }