import {createContext, type Context} from 'preact'; import {useContext} from 'preact/hooks'; export interface OptionalContext extends Context { readonly use: UseOptionalContextHook; } export interface OptionalContextOptions<_T> { displayName?: string; whenMissing?(): Error | void; } export interface UseOptionalContextHook { (options?: {optional?: false}): NonNullable; (options: {optional: boolean}): NonNullable | undefined; } /* #__NO_SIDE_EFFECTS__ */ export function createOptionalContext( defaultValue?: T, {whenMissing, displayName}: OptionalContextOptions = {}, ): OptionalContext { const Context = createContext(defaultValue); if (displayName) Object.assign(Context, {displayName}); const resolvedWhenMissing = whenMissing ?? (() => new Error(`Missing context: ${Context.displayName}`)); Object.assign(Context, { use({optional = false}: {optional?: boolean} = {}) { resolvedWhenMissing; const value = useContext(Context) ?? undefined; if (!optional && value == null) { throw resolvedWhenMissing(); } return value as any; }, }); return Context as OptionalContext; }