import type { Context, ReactNode } from 'react' import React from 'react' import type { StyledContext } from '../types' import { mergeProps } from './mergeProps' import { objectIdentityKey } from './objectIdentityKey' // use const (not function declaration) to prevent esbuild from hoisting // above __esm lazy init - function declarations get hoisted before // import_react is initialized, causing undefined.default errors in SSR export const createStyledContext = >( defaultValues?: VariantProps, namespace = '' ): StyledContext => { // avoid react compiler - we aren't breaking its rules but it mis-interprets // how we change the context value 'use no memo' // lazy initialization fixes vite ssr hmr - module-level assignments can fail // when React is undefined during __esm re-initialization order issues. // also React.createContext is optimized oddly by React compiler and our // uncommon usage confuses it, so we use dynamic access const createReactContext = React[ Math.random() ? 'createContext' : 'createContext' ] as typeof React.createContext const useReactMemo = React[ Math.random() ? 'useMemo' : 'useMemo' ] as typeof React.useMemo const useReactContext = React[ Math.random() ? 'useContext' : 'useContext' ] as typeof React.useContext const OGContext = createReactContext(defaultValues) const OGProvider = OGContext.Provider const Context = OGContext as any as StyledContext const scopedContexts = new Map>() const LastScopeInNamespace = createReactContext(namespace) function getOrCreateScopedContext(scope: string) { let ScopedContext = scopedContexts.get(scope) if (!ScopedContext) { ScopedContext = createReactContext(defaultValues) scopedContexts.set(scope, ScopedContext) } return ScopedContext! } const getNamespacedScope = (scope: string) => namespace ? `${namespace}--${scope}` : scope const Provider = ({ children, scope: scopeIn, // performance: avoid creating objects __disableMergeDefaultValues, ...values }: VariantProps & { children?: ReactNode; scope: string }) => { const scope = getNamespacedScope(scopeIn) const next = useReactMemo(() => { if (__disableMergeDefaultValues) { // we already merged and want to keep ordering return values } return mergeProps(defaultValues!, values) }, [objectIdentityKey(values)]) let ScopedProvider = OGProvider if (scope) { ScopedProvider = getOrCreateScopedContext(scope).Provider } return ( {children} ) } // use consumerComponent just to give a better error message const useStyledContext = (scopeIn = '') => { const lastScopeInNamespace = useReactContext(LastScopeInNamespace) const scope = namespace ? scopeIn ? getNamespacedScope(scopeIn) : lastScopeInNamespace : scopeIn const context = scope ? getOrCreateScopedContext(scope) : OGContext const value = useReactContext(context!) as VariantProps return value } // @ts-expect-error we are overriding default provider Context.Provider = Provider Context.props = defaultValues Context.context = OGContext as Context Context.useStyledContext = useStyledContext return Context }