import { createSimpleExpression , ConstantTypes } from './ast' ; import { isString , isArray } from '../shared' ; import { hoistStatic } from './transforms/hoistStatic' ; import { NodeTypes } from '../shared/svu' ; import { transformIf } from './transforms/vIf' ; import { transformElement } from './transforms/transformElement' ; import { transformOn } from './transforms/vOn' ; import { transformText } from './transforms/transformText' ; export function createTransformContext ( ) { const context = { hoists : [ ] , helpers : new Set ( ) , helper ( name ) { context . helpers . add ( name ) ; return name ; } , replaceNode ( node ) { context . parent ! . children [ context . childIndex ] = context . currentNode = node ; } , removeNode ( node ) { const list = context . parent ! . children ; const removalIndex = node ? list . indexOf ( node ) : context . currentNode ? context . childIndex : - 1 ; if ( ! node || node === context . currentNode ) { context . currentNode = null ; context . onNodeRemoved ( ) ; } else { if ( context . childIndex > removalIndex ) { context . childIndex -- ; context . onNodeRemoved ( ) ; } } context . parent ! . children . splice ( removalIndex , 1 ) ; } , hoist ( exp ) { context . hoists . push ( exp ) ; const identifier = createSimpleExpression ( `_hoisted_${ context . hoists . length }` , false , exp . loc , ConstantTypes . CAN_HOIST ) ; identifier . hoisted = exp ; return identifier ; } , nodeTransforms : [ transformIf , transformElement , transformText ] , directiveTransforms : { on : transformOn , } } ; return context ; } /**
 * 语法制导分析
 * traverseNode 顺序为后序遍历
 */ export function transform ( root ) { const context = createTransformContext ( ) ; traverseNode ( root , context ) ; hoistStatic ( root , context ) ; root . codegenNode = root . children [ 0 ] ; root . hoists = context . hoists ; } export function traverseNode ( node , context ) { context . currentNode = node ; const { nodeTransforms } = context ; const exitFns = [ ] ; for ( let i = 0 ; i < nodeTransforms . length ; i ++ ) { const onExit = nodeTransforms [ i ] ( node , context ) ; if ( onExit ) { if ( isArray ( onExit ) ) { exitFns . push ( ... onExit ) ; } else { exitFns . push ( onExit ) ; } } if ( ! context . currentNode ) { return ; } else { node = context . currentNode ; } } switch ( node . type ) { case NodeTypes . IF : for ( let i = 0 ; i < node . branches . length ; i ++ ) { traverseNode ( node . branches [ i ] , context ) ; } break ; case NodeTypes . IF_BRANCH : case NodeTypes . ELEMENT : case NodeTypes . ROOT : traverseChildren ( node , context ) ; break ; } context . currentNode = node ; let i = exitFns . length ; while ( i -- ) { exitFns [ i ] ( ) ; } } export function traverseChildren ( parent , context ) { let i = 0 ; const nodeRemoved = ( ) => { i -- ; } ; for ( ; i < parent . children . length ; i ++ ) { const child = parent . children [ i ] ; if ( isString ( child ) ) continue ; context . parent = parent ; context . childIndex = i ; context . onNodeRemoved = nodeRemoved ; traverseNode ( child , context ) ; } }