import { ComponentClass , Component , createContext } from 'react' ; import { interfaces } from 'inversify' ; type InversifyReactContextValue = interfaces . Container | undefined ; const InversifyReactContext = createContext < InversifyReactContextValue > ( undefined ) ; InversifyReactContext . displayName = 'InversifyReactContext' ; const contextTypeKey = 'contextType' ; const AdministrationKey = '~$inversify-react' ; type DiClassAdministration = { accepts : boolean ; } ; type DiInstanceAdministration = { container : interfaces . Container ; properties : { [ key ] : ( ) => unknown ; } ; } ; function getClassAdministration ( target ) { let administration = target [ AdministrationKey ] ; if ( ! administration ) { administration = { accepts : false , } ; Object . defineProperty ( target , AdministrationKey , { enumerable : false , writable : false , value : administration , } ) ; } return administration ; } function getInstanceAdministration ( target ) { let administration = target [ AdministrationKey ] ; if ( ! administration ) { const container = target . context as InversifyReactContextValue ; if ( ! container ) { throw new Error ( 'Cannot use resolve services without any providers in component tree.' ) ; } administration = { container , properties : { } , } ; Object . defineProperty ( target , AdministrationKey , { enumerable : false , writable : false , value : administration , } ) ; } return administration ; } function ensureAcceptContext ( target ) { const administration = getClassAdministration ( target ) ; if ( ! administration . accepts ) { const { contextType } = target ; const componentName = target . displayName || target . name ; if ( contextType ) { throw new Error ( 'inversify-react cannot configure React context.\n' + `Component \`${ componentName }\` already has \`${ contextTypeKey }: ${ contextType . displayName || '<anonymous context>' }\` defined.\n` + '@see inversify-react/test/resolve.tsx#limitations for more info and workarounds\n' ) ; } Object . defineProperty ( target , contextTypeKey , { enumerable : true , get ( ) { return InversifyReactContext ; } , set ( value ) { if ( value !== InversifyReactContext ) { throw new Error ( `Cannot change \`${ contextTypeKey }\` of \`${ componentName }\` component.\n` + 'Looks like you are using inversify-react decorators, ' + 'which have already patched this component and use own context to deliver IoC container.\n' + '@see inversify-react/test/resolve.tsx#limitations for more info and workarounds\n' ) ; } } } ) ; administration . accepts = true ; } } type PropertyOptions = Readonly < { isOptional ? : boolean ; defaultValue ? : unknown ; } > ; function createProperty ( target , name , type , options ) { Object . defineProperty ( target , name , { enumerable : true , get ( ) { const administration = getInstanceAdministration ( this ) ; let getter = administration . properties [ name ] ; if ( ! getter ) { const { container } = administration ; let value ; if ( options . isOptional ) { if ( container . isBound ( type ) ) { value = container . get ( type ) ; } else { value = options . defaultValue ; } } else { value = container . get ( type ) ; } getter = administration . properties [ name ] = ( ) => value ; } return getter ( ) ; } } ) ; const descriptor = Object . getOwnPropertyDescriptor ( target , name ) ; if ( ! descriptor ) throw new Error ( 'Failed to define property' ) ; return descriptor ; } export { InversifyReactContext , AdministrationKey , DiClassAdministration , DiInstanceAdministration , ensureAcceptContext , createProperty , PropertyOptions , getClassAdministration , getInstanceAdministration , } ;