import { RootNode , BlockStatement , TemplateLiteral , createCallExpression , createTemplateLiteral , NodeTypes , TemplateChildNode , ElementTypes , createBlockStatement , CompilerOptions , IfStatement , CallExpression , isText , processExpression , createSimpleExpression , createCompoundExpression , createTransformContext , createRoot } from '@vue/compiler-dom' ; import { isString , escapeHtml } from '@vue/shared' ; import { SSR_INTERPOLATE , ssrHelpers } from './runtimeHelpers' ; import { ssrProcessIf } from './transforms/ssrVIf' ; import { ssrProcessFor } from './transforms/ssrVFor' ; import { ssrProcessSlotOutlet } from './transforms/ssrTransformSlotOutlet' ; import { ssrProcessComponent } from './transforms/ssrTransformComponent' ; import { ssrProcessElement } from './transforms/ssrTransformElement' ; import { createSSRCompilerError , SSRErrorCodes } from './errors' ; export function ssrCodegenTransform ( ast , options ) { const context = createSSRTransformContext ( ast , options ) ; if ( options . ssrCssVars ) { const varsExp = processExpression ( createSimpleExpression ( options . ssrCssVars , false ) , createTransformContext ( createRoot ( [ ] ) , options ) ) ; context . body . push ( createCompoundExpression ( [ `const _cssVars = { style: ` , varsExp , `}` ] ) ) ; } const isFragment = ast . children . length > 1 && ast . children . some ( c => ! isText ( c ) ) ; processChildren ( ast . children , context , isFragment ) ; ast . codegenNode = createBlockStatement ( context . body ) ; ast . ssrHelpers = Array . from ( new Set ( [ ... ast . helpers . filter ( h => h in ssrHelpers ) , ... context . helpers ] ) ) ; ast . helpers = ast . helpers . filter ( h => ! ( h in ssrHelpers ) ) ; } export type SSRTransformContext = ReturnType < typeof createSSRTransformContext > ; function createSSRTransformContext ( root , options , helpers = new Set ( ) , withSlotScopeId = false ) { const body = [ ] ; let currentString = null ; return { root , options , body , helpers , withSlotScopeId , onError : options . onError || ( e => { throw e ; } ) , helper < T extends symbol > ( name ) { helpers . add ( name ) ; return name ; } , pushStringPart ( part ) { if ( ! currentString ) { const currentCall = createCallExpression ( `_push` ) ; body . push ( currentCall ) ; currentString = createTemplateLiteral ( [ ] ) ; currentCall . arguments . push ( currentString ) ; } const bufferedElements = currentString . elements ; const lastItem = bufferedElements [ bufferedElements . length - 1 ] ; if ( isString ( part ) && isString ( lastItem ) ) { bufferedElements [ bufferedElements . length - 1 ] += part ; } else { bufferedElements . push ( part ) ; } } , pushStatement ( statement ) { currentString = null ; body . push ( statement ) ; } } ; } function createChildContext ( parent , withSlotScopeId = parent . withSlotScopeId ) { return createSSRTransformContext ( parent . root , parent . options , parent . helpers , withSlotScopeId ) ; } export function processChildren ( children , context , asFragment = false , disableNestedFragments = false ) { if ( asFragment ) { context . pushStringPart ( `<!--[-->` ) ; } for ( let i = 0 ; i < children . length ; i ++ ) { const child = children [ i ] ; switch ( child . type ) { case NodeTypes . ELEMENT : switch ( child . tagType ) { case ElementTypes . ELEMENT : ssrProcessElement ( child , context ) ; break ; case ElementTypes . COMPONENT : ssrProcessComponent ( child , context ) ; break ; case ElementTypes . SLOT : ssrProcessSlotOutlet ( child , context ) ; break ; case ElementTypes . TEMPLATE : break ; default : context . onError ( createSSRCompilerError ( SSRErrorCodes . X_SSR_INVALID_AST_NODE , ( child as any ) . loc ) ) ; const exhaustiveCheck = child ; return exhaustiveCheck ; } break ; case NodeTypes . TEXT : context . pushStringPart ( escapeHtml ( child . content ) ) ; break ; case NodeTypes . COMMENT : context . pushStringPart ( `<!--${ child . content }-->` ) ; break ; case NodeTypes . INTERPOLATION : context . pushStringPart ( createCallExpression ( context . helper ( SSR_INTERPOLATE ) , [ child . content ] ) ) ; break ; case NodeTypes . IF : ssrProcessIf ( child , context , disableNestedFragments ) ; break ; case NodeTypes . FOR : ssrProcessFor ( child , context , disableNestedFragments ) ; break ; case NodeTypes . IF_BRANCH : break ; case NodeTypes . TEXT_CALL : case NodeTypes . COMPOUND_EXPRESSION : break ; default : context . onError ( createSSRCompilerError ( SSRErrorCodes . X_SSR_INVALID_AST_NODE , ( child as any ) . loc ) ) ; const exhaustiveCheck = child ; return exhaustiveCheck ; } } if ( asFragment ) { context . pushStringPart ( `<!--]-->` ) ; } } export function processChildrenAsStatement ( children , parentContext , asFragment = false , withSlotScopeId = parentContext . withSlotScopeId ) { const childContext = createChildContext ( parentContext , withSlotScopeId ) ; processChildren ( children , childContext , asFragment ) ; return createBlockStatement ( childContext . body ) ; }