import faker from 'faker' ; import deepmerge from 'deepmerge' ; import { Factoria } from './types' ; const definitions = { } ; const isDictionary = ( thingy ) => Object . prototype . toString . call ( thingy ) === '[object Object]' ; let appliedStates = [ ] ; const resolveOverrides = < T > ( overrides ) : Object => { const props = Object . assign ( { } , overrides ) as Factoria . Attributes ; for ( const key in props ) { if ( ! Object . prototype . hasOwnProperty . call ( props , key ) ) { continue ; } if ( props [ key ] instanceof Function ) { props [ key ] = props [ key ] . call ( this , faker ) ; } else if ( isDictionary ( props [ key ] ) ) { props [ key ] = resolveOverrides ( props [ key ] ) ; } } return props ; } ; const generate = < T > ( name , overrides = { } , states ) : T => { const statesBackup = appliedStates ; appliedStates = [ ] ; let stateAttributes = { } ; states . forEach ( state => { if ( ! Object . prototype . hasOwnProperty . call ( definitions [ name ] . states , state ) ) { throw new Error ( `Model "${ name }" has no "${ state }" state.` ) ; } const stateDescriptor = definitions [ name ] . states [ state ] ; stateAttributes = deepmerge ( stateAttributes , stateDescriptor instanceof Function ? stateDescriptor . call ( this , faker ) : stateDescriptor ) ; } ) ; const result = deepmerge . all ( [ definitions [ name ] . attributes ( faker ) , stateAttributes , resolveOverrides < T > ( overrides ) ] ) as unknown as T ; appliedStates = statesBackup ; return result ; } ; const factory = < T > ( name , count = 1 , overrides = { } ) : T | T [ ] => { if ( ! Object . prototype . hasOwnProperty . call ( definitions , name ) ) { throw new Error ( `Model "${ name }" not found.` ) ; } if ( typeof count !== 'number' ) { return factory ( name , 1 , count ) ; } const generated = count === 1 ? generate ( name , overrides , appliedStates ) : Array . from ( Array ( count ) ) . map ( ( ) => generate ( name , overrides , appliedStates ) ) ; appliedStates = [ ] ; return generated ; } ; factory . states = ( ... states ) => { appliedStates = states ; return factory ; } ; factory . define = < T > ( name , attributes , states = { } ) => { definitions [ name ] = { attributes , states } ; return factory ; } ; export default factory ;