/**
 * For determining which components and their inputs/outputs are referenced by
 * expressions.
 */ import * as _ from 'lodash' ; import { AppComponentDefinition , ComponentInstance , InInput } from './datatypes' ; import compileDvExpr from './expression.compiler' ; export interface Reference { component : ComponentInstance ; ioName : string ; } export interface InReferences { [ componentID ] : { [ ioName ] : Reference [ ] ; } ; } export interface OutReferences { [ componentID ] : Reference [ ] ; } export function resolveName ( name , appComponentInstance , inInput ? ) { if ( ! ( appComponentInstance . of instanceof AppComponentDefinition ) ) { throw new Error ( 'Component is not an app component.' ) ; } const appComponentDefinition = appComponentInstance . of ; let ioName ; let fromComponent ; if ( name [ 0 ] . startsWith ( '$' ) ) { ioName = name . slice ( 1 ) ; if ( inInput && inInput . of . of . componentInputs [ inInput . name ] [ ioName ] ) { fromComponent = inInput . of ; } else if ( appComponentDefinition . inputs . includes ( ioName ) ) { fromComponent = appComponentInstance ; } } else { let conceptN ; let componentN ; [ conceptN , componentN , ioName ] = name . split ( '.' ) ; const maybeFA = appComponentDefinition . findChild ( conceptN , componentN ) ; if ( maybeFA && maybeFA . of . outputs . includes ( ioName ) ) { fromComponent = maybeFA ; } } return { fromComponent , ioName } ; } export default function findReferences ( appComponentInstance ) { if ( ! ( appComponentInstance . of instanceof AppComponentDefinition ) ) { throw new Error ( 'Component is not an app component.' ) ; } const appComponentDefinition = appComponentInstance . of ; const inReferences = { } ; const outReferences = { } ; appComponentDefinition . getChildren ( true ) . forEach ( ( component ) => { component . walkInputs ( true , ( inputName , inputValue , ofComponent , inInput ) => { if ( ! _ . isString ( inputValue ) ) { return ; } let compiledDvExpr ; try { compiledDvExpr = compileDvExpr ( inputValue ) ; } catch ( e ) { console . error ( `Coulnd't find references for expression "${ inputValue }",` + `used in component "${ component . of . name }" for input ` + `"${ inputName }"` ) ; console . error ( e ) ; return ; } const { names } = compiledDvExpr ; names . forEach ( ( name ) => { const { fromComponent , ioName } = resolveName ( name , appComponentInstance , inInput ) ; if ( fromComponent ) { let ioInReferences = ( inReferences [ ofComponent . id ] || { } ) [ inputName ] ; if ( ! ioInReferences ) { ioInReferences = [ ] ; _ . set ( inReferences , [ ofComponent . id , inputName ] , ioInReferences ) ; } if ( ! ioInReferences . find ( ( r ) => r . ioName === ioName && r . component . id === fromComponent . id ) ) { ioInReferences . push ( { component : fromComponent , ioName } ) ; } let componentOutReferences = outReferences [ fromComponent . id ] ; if ( ! componentOutReferences ) { componentOutReferences = [ ] ; outReferences [ fromComponent . id ] = componentOutReferences ; } if ( ! componentOutReferences . find ( ( r ) => ( r . ioName === ioName && r . component . id === ofComponent . id ) ) ) { componentOutReferences . push ( { ioName , component : ofComponent } ) ; } } } ) ; } ) ; } ) ; return { inReferences , outReferences } ; }