import { View } from '../../core/view' ; import { isEventOrGesture } from '../../core/bindable' ; import { getBindingOptions , bindingConstants } from '../binding-builder' ; import { profile } from '../../../profiling' ; import * as debugModule from '../../../utils/debug' ; import { Device } from '../../../platform' ; import { sanitizeModuleName } from '../module-name-sanitizer' ; import { resolveModuleName } from '../../../module-name-resolver' ; export interface ComponentModule { component : View ; exports : any ; } const legacyShortBarrels = [ 'text/formatted-string' , 'text/span' , 'ui/text-base/formatted-string' , 'ui/text-base/span' , 'ui/action-bar' , 'ui/activity-indicator' , 'ui/button' , 'ui/content-view' , 'ui/date-picker' , 'ui/frame' , 'ui/html-view' , 'ui/image' , 'ui/label' , 'ui/layouts/absolute-layout' , 'ui/layouts/dock-layout' , 'ui/layouts/grid-layout' , 'ui/layouts/stack-layout' , 'ui/layouts/flexbox-layout' , 'ui/layouts/wrap-layout' , 'ui/list-picker' , 'ui/page' , 'ui/placeholder' , 'ui/progress' , 'ui/proxy-view-container' , 'ui/repeater' , 'ui/scroll-view' , 'ui/search-bar' , 'ui/segmented-bar' , 'ui/slider' , 'ui/switch' , 'ui/tab-view' , 'ui/web-view' , 'ui/text-field' , 'ui/text-view' , 'ui/time-picker' , 'ui/list-view' , ] ; const CORE_UI_BARREL = '@nativescript/core/ui' ; const CODE_FILE = 'codeFile' ; const CSS_FILE = 'cssFile' ; const IMPORT = 'import' ; const createComponentInstance = profile ( 'createComponentInstance' , ( elementName , namespace ) : { instance : View ; instanceModule : Object ; } => { let instance ; let instanceModule ; let resolvedModuleName ; try { if ( typeof namespace === 'string' ) { if ( legacyShortBarrels . includes ( namespace ) ) { resolvedModuleName = CORE_UI_BARREL ; } else { resolvedModuleName = resolveModuleName ( namespace , '' ) ; } instanceModule = global . loadModule ( resolvedModuleName , true ) ; } else { instanceModule = global . loadModule ( CORE_UI_BARREL , false ) ; } const instanceType = instanceModule [ elementName ] ; instance = new instanceType ( ) ; } catch ( ex ) { const debug = require ( '../../../utils/debug' ) ; throw new debug . ScopeError ( ex , "Module '" + ( resolvedModuleName || elementName ) + "' not found for element '" + ( namespace ? namespace + ':' : '' ) + elementName + "'." ) ; } return { instance , instanceModule } ; } ) ; const getComponentModuleExports = profile ( 'getComponentModuleExports' , ( instance , moduleExports , attributes ) : Object => { if ( attributes ) { const codeFileAttribute = attributes [ CODE_FILE ] || attributes [ IMPORT ] ; if ( codeFileAttribute ) { const resolvedCodeFileModule = resolveModuleName ( sanitizeModuleName ( codeFileAttribute ) , '' ) ; if ( resolvedCodeFileModule ) { moduleExports = global . loadModule ( resolvedCodeFileModule , true ) ; ( < any > instance ) . exports = moduleExports ; } else { throw new Error ( `Code file with path "${ codeFileAttribute }" cannot be found! Looking for webpack module with name "${ resolvedCodeFileModule }"` ) ; } } } return moduleExports ; } ) ; const applyComponentCss = profile ( 'applyComponentCss' , ( instance , moduleName , attributes ) => { let cssApplied = false ; if ( attributes && attributes [ CSS_FILE ] ) { const resolvedCssModuleName = resolveModuleName ( sanitizeModuleName ( attributes [ CSS_FILE ] ) , 'css' ) ; if ( resolvedCssModuleName ) { instance . addCssFile ( resolvedCssModuleName ) ; cssApplied = true ; } else { throw new Error ( `Css file with path "${ attributes [ CSS_FILE ] }" cannot be found! Looking for webpack module with name "${ resolvedCssModuleName }"` ) ; } } if ( moduleName && ! cssApplied ) { const resolvedCssModuleName = resolveModuleName ( moduleName , 'css' ) ; if ( resolvedCssModuleName ) { instance . addCssFile ( resolvedCssModuleName ) ; } } } ) ; const applyComponentAttributes = profile ( 'applyComponentAttributes' , ( instance , instanceModule , moduleExports , attributes ) => { if ( instance && instanceModule ) { for ( let attr in attributes ) { const attrValue = < string > attributes [ attr ] ; if ( attr . indexOf ( ':' ) !== - 1 ) { const platformName = attr . split ( ':' ) [ 0 ] . trim ( ) ; if ( platformName . toLowerCase ( ) === Device . os . toLowerCase ( ) ) { attr = attr . split ( ':' ) [ 1 ] . trim ( ) ; } else { continue ; } } if ( attr . indexOf ( '.' ) !== - 1 ) { let subObj = instance ; const properties = attr . split ( '.' ) ; const subPropName = properties [ properties . length - 1 ] ; for ( let i = 0 ; i < properties . length - 1 ; i ++ ) { if ( subObj !== undefined && subObj !== null ) { subObj = subObj [ properties [ i ] ] ; } } if ( subObj !== undefined && subObj !== null ) { setPropertyValue ( subObj , instanceModule , moduleExports , subPropName , attrValue ) ; } } else { setPropertyValue ( instance , instanceModule , moduleExports , attr , attrValue ) ; } } } } ) ; export function getComponentModule ( elementName , namespace , attributes , moduleExports , moduleNamePath ? , isRootComponent ? ) { elementName = elementName . split ( '-' ) . map ( ( s ) => s [ 0 ] . toUpperCase ( ) + s . substring ( 1 ) ) . join ( '' ) ; const { instance , instanceModule } = createComponentInstance ( elementName , namespace , null ) ; moduleExports = getComponentModuleExports ( instance , < any > moduleExports , attributes ) ; if ( isRootComponent ) { applyComponentCss ( instance , moduleNamePath , attributes ) ; } applyComponentAttributes ( instance , instanceModule , moduleExports , attributes ) ; let componentModule ; if ( instance && instanceModule ) { componentModule = { component : instance , exports : instanceModule } ; } return componentModule ; } export function setPropertyValue ( instance , instanceModule , exports , propertyName , propertyValue ) { if ( isBinding ( propertyValue ) && instance . bind ) { const bindOptions = getBindingOptions ( propertyName , getBindingExpressionFromAttribute ( propertyValue ) ) ; instance . bind ( { sourceProperty : bindOptions [ bindingConstants . sourceProperty ] , targetProperty : bindOptions [ bindingConstants . targetProperty ] , expression : bindOptions [ bindingConstants . expression ] , twoWay : bindOptions [ bindingConstants . twoWay ] , } , bindOptions [ bindingConstants . source ] ) ; } else if ( isEventOrGesture ( propertyName , instance ) ) { const handler = exports && exports [ propertyValue ] ; if ( typeof handler === 'function' ) { instance . on ( propertyName , handler ) ; } } else if ( isKnownFunction ( propertyName , instance ) && exports && typeof exports [ propertyValue ] === 'function' ) { instance [ propertyName ] = exports [ propertyValue ] ; } else { instance [ propertyName ] = propertyValue ; } } function getBindingExpressionFromAttribute ( value ) { return value . replace ( '{{' , '' ) . replace ( '}}' , '' ) . trim ( ) ; } function isBinding ( value ) { let isBinding ; if ( typeof value === 'string' ) { const str = value . trim ( ) ; isBinding = str . indexOf ( '{{' ) === 0 && str . lastIndexOf ( '}}' ) === str . length - 2 ; } return isBinding ; } const KNOWN_FUNCTIONS = 'knownFunctions' ; function isKnownFunction ( name , instance ) { return instance . constructor && KNOWN_FUNCTIONS in instance . constructor && instance . constructor [ KNOWN_FUNCTIONS ] . indexOf ( name ) !== - 1 ; }