import { stringify } from 'flatted' ; import m , { Children , Component , Vnode , VnodeDOM } from 'mithril' ; import type { MithrilHooks , ReactTypes } from './types' ; let currentState ; const call = Function . prototype . call . bind ( Function . prototype . call ) ; const scheduleRender = ( ) => m . redraw ( ) ; const updateDeps = ( deps ? ) => { const state = currentState ; const { depsIndex } = state ; state . depsIndex += 1 ; const prevDeps = state . depsStates [ depsIndex ] || [ ] ; const shouldRecompute = deps === undefined ? true : Array . isArray ( deps ) ? deps . length > 0 ? ! deps . every ( ( x , i ) => x === prevDeps [ i ] ) : ! state . setup : false ; if ( deps !== undefined ) { state . depsStates [ depsIndex ] = deps ; } return shouldRecompute ; } ; const effect = ( isAsync = false ) => ( fn , deps ? ) => { const state = currentState ; const shouldRecompute = updateDeps ( deps ) ; if ( shouldRecompute ) { const { depsIndex } = state ; const runCallbackFn = ( ) => { const teardown = fn ( ) ; if ( typeof teardown === 'function' ) { state . teardowns . set ( depsIndex , teardown as MithrilHooks . EffectReturnFn ) ; state . teardowns . set ( '_' , scheduleRender ) ; } } ; const teardown = state . teardowns . get ( depsIndex ) ; try { if ( typeof teardown === 'function' ) { teardown ( ) ; } } finally { state . teardowns . delete ( depsIndex ) ; } state . updates . push ( isAsync ? ( ) => new Promise ( resolve => requestAnimationFrame ( resolve ) ) . then ( runCallbackFn ) : runCallbackFn ) ; } } ; const updateState = < T > ( initialState ? , newValueFn ? ) : [ T , ( value : MithrilHooks . ValueOrFn < T > ) => unknown , number ] => { const state = currentState ; const index = state . statesIndex ; state . statesIndex += 1 ; if ( ! state . setup ) { state . states [ index ] = initialState ; } return [ state . states [ index ] as T , ( value ) => { const previousValue = state . states [ index ] ; const newValue = newValueFn ? newValueFn ( value as T , index ) : value ; state . states [ index ] = newValue ; if ( stringify ( newValue ) !== stringify ( previousValue ) ) { scheduleRender ( ) ; } } , index , ] ; } ; export const useState = < T = unknown > ( initialState ? ) : [ T , ( value : MithrilHooks . ValueOrFn < T > ) => unknown , number ] => { const state = currentState ; const newValueFn = ( value , index ) => typeof value === 'function' ? ( value as MithrilHooks . ValueFn < T > ) ( state . states [ index ] as T , index ) : value ; return updateState < T > ( initialState , newValueFn ) ; } ; export const useEffect = effect ( true ) ; export const useLayoutEffect = effect ( ) ; export function useReducer < T , A = unknown , U = unknown > ( reducer , initialState , initFn ) ; export function useReducer < T , A = unknown , U = unknown > ( reducer , initialState ? , initFn ? ) ; export function useReducer < T , A = unknown , U = unknown > ( reducer , initialState ? , initFn ? ) { const state = currentState ; const initValue = ! state . setup && initFn ? ( initFn ( initialState as U ) as T ) : ( initialState as T ) ; const getValueDispatch = ( ) : [ T , ( action : A ) => unknown ] => { const [ value , setValue , index ] = updateState ( initValue ) ; const dispatch = ( action ) => { const previousValue = state . states [ index ] as T ; return setValue ( reducer ( previousValue , action ) ) ; } ; return [ value , dispatch ] ; } ; return getValueDispatch ( ) ; } export const useRef = < T = unknown > ( initialValue ? ) => { const [ value ] = updateState < { current : T | undefined ; } > ( { current : initialValue } ) ; return value ; } ; export const useMemo = < T = unknown > ( fn , deps ? ) => { const state = currentState ; const shouldRecompute = updateDeps ( deps ) ; const [ memoized , setMemoized ] = ! state . setup ? updateState < T > ( fn ( ) ) : updateState < T > ( ) ; if ( state . setup && shouldRecompute ) { setMemoized ( fn ( ) ) ; } return memoized ; } ; export const useCallback = < T extends ( ... args : unknown [ ] ) => unknown > ( callback , deps ? ) => useMemo ( ( ) => callback , deps ) ; export const withHooks = < T = unknown > ( renderFunction , initialAttrs ? ) : Component < T , MithrilHooks . State > => { const init = ( vnode ) => { Object . assign ( vnode . state , { setup : false , states : [ ] , statesIndex : 0 , depsStates : [ ] , depsIndex : 0 , updates : [ ] , cleanups : new Map ( ) , teardowns : new Map ( ) } ) ; } ; const update = ( vnode ) => { const prevState = currentState ; currentState = vnode . state ; try { vnode . state . updates . forEach ( call ) ; } finally { Object . assign ( vnode . state , { setup : true , updates : [ ] , depsIndex : 0 , statesIndex : 0 } ) ; currentState = prevState ; } } ; const render = ( vnode ) => { const prevState = currentState ; currentState = vnode . state ; try { return renderFunction ( { ... initialAttrs , ... vnode . attrs , vnode , children : vnode . children } ) ; } catch ( e ) { console . error ( e ) ; } finally { currentState = prevState ; } } ; const teardown = ( vnode ) => { const prevState = currentState ; currentState = vnode . state ; try { vnode . state . teardowns . forEach ( call ) ; } finally { currentState = prevState ; } } ; return { oninit : init , oncreate : update , onupdate : update , view : render , onremove : teardown } ; } ;