import { ExtractRematchStateFromModels , Model , Models , Plugin , RematchStore , } from '@rematch/core' ; import { createSelector , createStructuredSelector } from 'reselect' ; import { SelectConfig } from './types' ; const makeSelect = ( ) => { param mapSelectToStructure param structuredSelectorCreator return function select ( mapSelectToStructure , structuredSelectorCreator = createStructuredSelector ) { let func = ( state , props ) : any => { func = structuredSelectorCreator ( mapSelectToStructure ( select ) ) ; return func ( state , props ) ; } ; return ( state , props ) => func ( state , props ) ; } return select ; } ; const makeFactoryGroup = ( ) => { let ready = false ; const factories = new Set ( ) ; return { add ( added ) { if ( ! ready ) { added . forEach ( ( factory ) => factories . add ( factory ) ) ; } else { added . forEach ( ( factory ) => factory ( ) ) ; } } , finish ( factory ) { factories . delete ( factory ) ; } , startBuilding ( ) { ready = true ; factories . forEach ( ( factory ) => factory ( ) ) ; } } ; } ; const validateConfig = < TModels extends Models < TModels > , TExtraModels extends Models < TModels > > ( config ) : void => { if ( process . env . NODE_ENV !== 'production' ) { if ( config . sliceState && typeof config . sliceState !== 'function' ) { throw new Error ( 'select plugin config sliceState must be a function' ) ; } if ( config . selectorCreator && typeof config . selectorCreator !== 'function' ) { throw new Error ( 'select plugin config selectorCreator must be a function' ) ; } } } ; const validateSelector = ( selectorFactories , selectorName , model ) : void => { if ( process . env . NODE_ENV !== 'production' ) { if ( typeof selectorFactories ?. [ selectorName ] !== 'function' ) { throw new Error ( `Selector (${ model . name }/${ selectorName }) must be a function` ) ; } } } ; const createSelectPlugin = < TModels extends Models < TModels > , TExtraModels extends Models < TModels > = Record < string , any > > ( config = { } ) : Plugin < TModels , TExtraModels > => { validateConfig ( config ) ; const sliceState = config . sliceState || ( ( state , model ) => state [ model . name || '' ] ) ; const selectorCreator = config . selectorCreator || createSelector ; const slice = ( model ) => ( stateOrNext ) => { if ( typeof stateOrNext === 'function' ) { return selectorCreator ( ( state ) => sliceState ( state , model ) , stateOrNext ) ; } return sliceState ( stateOrNext , model ) ; } ; const hasProps = ( inner ) => function ( this , models ) { return selectorCreator ( ( props ) => props , ( props ) => inner . call ( this , models , props ) ) ; } ; const factoryGroup = makeFactoryGroup ( ) ; const select = makeSelect ( ) ; return { exposed : { select , sliceState , selectorCreator } , onModel ( model ) { select [ model . name ] = { } ; const selectorFactories = typeof model . selectors === 'function' ? model . selectors ( slice ( model ) , selectorCreator , hasProps ) : model . selectors ; factoryGroup . add ( Object . keys ( selectorFactories || { } ) . map ( ( selectorName ) => { validateSelector ( selectorFactories , selectorName , model ) ; const factory = ( ) => { factoryGroup . finish ( factory ) ; delete select [ model . name ] [ selectorName ] ; select [ model . name ] [ selectorName ] = selectorFactories [ selectorName ] . call ( select [ model . name ] , select ) ; return select [ model . name ] [ selectorName ] ; } ; Object . defineProperty ( select [ model . name ] , selectorName , { configurable : true , get ( ) { return factory ( ) ; } } ) ; return factory ; } ) ) ; } , onStoreCreated ( store ) { factoryGroup . startBuilding ( ) ; store . select = select ; } } ; } ; export default createSelectPlugin ; export * from './types' ;