import { isIdentifier , isFunction , literal } from '../utils' ; import { ENDGetterPathFragment , Expression , MemberExpression , ENDGetter , CallExpression , ENDCaller , ENDFilter , JSNode , ArrowFunctionExpression , ArrayExpression , ENDGetterPrefix , IdentifierContext , Identifier , Literal } from '../ast' ; import { JSParserOptions } from '.' ; /**
 * Tries to convert some JS nodes like `MemberExpression` or `CallExpression` into
 * special Endorphin-specific nodes for safe data access
 */ export function convert ( node , options = { } ) { if ( isMemberExpression ( node ) ) { if ( isFilter ( node ) ) { return createFilter ( node ) ; } if ( ! options . disableGetters ) { return createGetter ( node ) ; } } else if ( isCallExpression ( node ) && ! options . disableCallers ) { return createCaller ( node ) ; } return node ; } /**
 * Collects plain path for value getter, if possible
 */ export function createGetter ( expr ) { const result = { type : 'ENDGetter' , start : expr . start , end : expr . end , path : [ ] } ; let ctx = expr ; while ( ctx ) { if ( isMemberExpression ( ctx ) ) { if ( isFilter ( ctx ) ) { result . path . unshift ( createFilter ( ctx ) ) ; break ; } if ( isIdentifier ( ctx . property ) && ! ctx . computed ) { result . path . unshift ( idToLiteral ( ctx . property ) ) ; } else { result . path . unshift ( convert ( ctx . property ) ) ; } ctx = ctx . object ; } else { if ( isIdentifier ( ctx ) && ! ctx . context ) { return expr ; } result . path . unshift ( convert ( ctx ) ) ; ctx = null ; } } return result ; } function idToLiteral ( id ) { return literal ( id . name , null , { start : id . start , end : id . end , loc : id . loc } ) ; } export function createCaller ( expr ) { const { callee } = expr ; if ( isIdentifier ( callee ) && callee . context ) { if ( callee . context === 'helper' ) { return { ... expr , arguments : [ { type : 'ThisExpression' , start : expr . start , end : expr . end } , ... expr . arguments ] } ; } if ( callee . context !== 'store' ) { return { type : 'ENDCaller' , start : callee . start , end : callee . end , object : getterPrefix ( callee . context ) , property : idToLiteral ( callee ) , arguments : expr . arguments } ; } } if ( isMemberExpression ( callee ) ) { if ( isIdentifier ( callee . object ) && ! callee . object . context ) { return expr ; } return { type : 'ENDCaller' , start : callee . start , end : callee . end , object : convert ( callee . object ) , property : isIdentifier ( callee . property ) ? idToLiteral ( callee . property ) : callee . property , arguments : expr . arguments } ; } return expr ; } function createFilter ( expr ) { const { property , object } = expr ; const filter = ( isArray ( property ) ? property . elements [ 0 ] : property ) as ArrowFunctionExpression ; return { type : 'ENDFilter' , start : expr . start , end : expr . end , object : convert ( object ) , expression : filter , multiple : isArray ( property ) } ; } function isFilter ( expr ) { return isFunction ( expr . property ) || isArray ( expr . property ) && expr . property . elements . length && isFunction ( expr . property . elements [ 0 ] ) ; } function isMemberExpression ( node ) { return node . type === 'MemberExpression' ; } function isCallExpression ( node ) { return node . type === 'CallExpression' ; } function isArray ( node ) { return node . type === 'ArrayExpression' ; } function getterPrefix ( context ) { return { type : 'ENDGetterPrefix' , start : 0 , end : 0 , context } ; }