@ -69,6 +69,13 @@ export interface CodegenResult {
map? : RawSourceMap
}
const enum NewlineType {
Start = 0 ,
End = - 1 ,
None = - 2 ,
Unknown = - 3
}
export interface CodegenContext
extends Omit < Required < CodegenOptions > , 'bindingMetadata' | 'inline' > {
source : string
@ -80,7 +87,7 @@ export interface CodegenContext
pure : boolean
map? : SourceMapGenerator
helper ( key : symbol ) : string
push ( code : string , node? : CodegenNode ) : void
push ( code : string , newlineIndex? : number , n ode? : CodegenNode ) : void
indent ( ) : void
deindent ( withoutNewLine? : boolean ) : void
newline ( ) : void
@ -127,7 +134,7 @@ function createCodegenContext(
helper ( key ) {
return ` _ ${ helperNameMap [ key ] } `
} ,
push ( code , node ) {
push ( code , newlineIndex = NewlineType . None , n ode ) {
context . code += code
if ( ! __BROWSER__ && context . map ) {
if ( node ) {
@ -140,7 +147,41 @@ function createCodegenContext(
}
addMapping ( node . loc . start , name )
}
advancePositionWithMutation ( context , code )
if ( newlineIndex === NewlineType . Unknown ) {
// multiple newlines, full iteration
advancePositionWithMutation ( context , code )
} else {
// fast paths
context . offset += code . length
if ( newlineIndex === NewlineType . None ) {
// no newlines; fast path to avoid newline detection
if ( __TEST__ && code . includes ( '\n' ) ) {
throw new Error (
` CodegenContext.push() called newlineIndex: none, but contains ` +
` newlines: ${ code . replace ( /\n/g , '\\n' ) } `
)
}
context . column += code . length
} else {
// single newline at known index
if ( newlineIndex === NewlineType . End ) {
newlineIndex = code . length - 1
}
if (
__TEST__ &&
( code . charAt ( newlineIndex ) !== '\n' ||
code . slice ( 0 , newlineIndex ) . includes ( '\n' ) ||
code . slice ( newlineIndex + 1 ) . includes ( '\n' ) )
) {
throw new Error (
` CodegenContext.push() called with newlineIndex: ${ newlineIndex } ` +
` but does not conform: ${ code . replace ( /\n/g , '\\n' ) } `
)
}
context . line ++
context . column = code . length - newlineIndex
}
}
if ( node && node . loc !== locStub ) {
addMapping ( node . loc . end )
}
@ -162,7 +203,7 @@ function createCodegenContext(
}
function newline ( n : number ) {
context . push ( '\n' + ` ` . repeat ( n ) )
context . push ( '\n' + ` ` . repeat ( n ) , NewlineType . Start )
}
function addMapping ( loc : Position , name? : string ) {
@ -250,8 +291,10 @@ export function generate(
// function mode const declarations should be inside with block
// also they should be renamed to avoid collision with user properties
if ( hasHelpers ) {
push ( ` const { ${ helpers . map ( aliasHelper ) . join ( ', ' ) } } = _Vue ` )
push ( ` \ n ` )
push (
` const { ${ helpers . map ( aliasHelper ) . join ( ', ' ) } } = _Vue \ n ` ,
NewlineType . End
)
newline ( )
}
}
@ -282,7 +325,7 @@ export function generate(
}
}
if ( ast . components . length || ast . directives . length || ast . temps ) {
push ( ` \ n ` )
push ( ` \ n ` , NewlineType . Start )
newline ( )
}
@ -334,11 +377,14 @@ function genFunctionPreamble(ast: RootNode, context: CodegenContext) {
const helpers = Array . from ( ast . helpers )
if ( helpers . length > 0 ) {
if ( ! __BROWSER__ && prefixIdentifiers ) {
push ( ` const { ${ helpers . map ( aliasHelper ) . join ( ', ' ) } } = ${ VueBinding } \ n ` )
push (
` const { ${ helpers . map ( aliasHelper ) . join ( ', ' ) } } = ${ VueBinding } \ n ` ,
NewlineType . End
)
} else {
// "with" mode.
// save Vue in a separate variable to avoid collision
push ( ` const _Vue = ${ VueBinding } \ n ` )
push ( ` const _Vue = ${ VueBinding } \ n ` , NewlineType . End )
// in "with" mode, helpers are declared inside the with block to avoid
// has check cost, but hoists are lifted out of the function - we need
// to provide the helper here.
@ -353,7 +399,7 @@ function genFunctionPreamble(ast: RootNode, context: CodegenContext) {
. filter ( helper = > helpers . includes ( helper ) )
. map ( aliasHelper )
. join ( ', ' )
push ( ` const { ${ staticHelpers } } = _Vue \ n ` )
push ( ` const { ${ staticHelpers } } = _Vue \ n ` , NewlineType . End )
}
}
}
@ -363,7 +409,8 @@ function genFunctionPreamble(ast: RootNode, context: CodegenContext) {
push (
` const { ${ ast . ssrHelpers
. map ( aliasHelper )
. join ( ', ' ) } } = require ( "${ssrRuntimeModuleName}" ) \ n `
. join ( ', ' ) } } = require ( "${ssrRuntimeModuleName}" ) \ n ` ,
NewlineType . End
)
}
genHoists ( ast . hoists , context )
@ -402,18 +449,21 @@ function genModulePreamble(
push (
` import { ${ helpers
. map ( s = > helperNameMap [ s ] )
. join ( ', ' ) } } from $ { JSON . stringify ( runtimeModuleName ) } \ n `
. join ( ', ' ) } } from $ { JSON . stringify ( runtimeModuleName ) } \ n ` ,
NewlineType . End
)
push (
` \ n// Binding optimization for webpack code-split \ nconst ${ helpers
. map ( s = > ` _ ${ helperNameMap [ s ] } = ${ helperNameMap [ s ] } ` )
. join ( ', ' ) } \ n `
. join ( ', ' ) } \ n ` ,
NewlineType . End
)
} else {
push (
` import { ${ helpers
. map ( s = > ` ${ helperNameMap [ s ] } as _ ${ helperNameMap [ s ] } ` )
. join ( ', ' ) } } from $ { JSON . stringify ( runtimeModuleName ) } \ n `
. join ( ', ' ) } } from $ { JSON . stringify ( runtimeModuleName ) } \ n ` ,
NewlineType . End
)
}
}
@ -422,7 +472,8 @@ function genModulePreamble(
push (
` import { ${ ast . ssrHelpers
. map ( s = > ` ${ helperNameMap [ s ] } as _ ${ helperNameMap [ s ] } ` )
. join ( ', ' ) } } from "${ssrRuntimeModuleName}" \ n `
. join ( ', ' ) } } from "${ssrRuntimeModuleName}" \ n ` ,
NewlineType . End
)
}
@ -554,7 +605,7 @@ function genNodeList(
for ( let i = 0 ; i < nodes . length ; i ++ ) {
const node = nodes [ i ]
if ( isString ( node ) ) {
push ( node )
push ( node , NewlineType . Unknown )
} else if ( isArray ( node ) ) {
genNodeListAsArray ( node , context )
} else {
@ -573,7 +624,7 @@ function genNodeList(
function genNode ( node : CodegenNode | symbol | string , context : CodegenContext ) {
if ( isString ( node ) ) {
context . push ( node )
context . push ( node , NewlineType . Unknown )
return
}
if ( isSymbol ( node ) ) {
@ -671,12 +722,16 @@ function genText(
node : TextNode | SimpleExpressionNode ,
context : CodegenContext
) {
context . push ( JSON . stringify ( node . content ) , node )
context . push ( JSON . stringify ( node . content ) , NewlineType . Unknown , node )
}
function genExpression ( node : SimpleExpressionNode , context : CodegenContext ) {
const { content , isStatic } = node
context . push ( isStatic ? JSON . stringify ( content ) : content , node )
context . push (
isStatic ? JSON . stringify ( content ) : content ,
NewlineType . Unknown ,
node
)
}
function genInterpolation ( node : InterpolationNode , context : CodegenContext ) {
@ -694,7 +749,7 @@ function genCompoundExpression(
for ( let i = 0 ; i < node . children ! . length ; i ++ ) {
const child = node . children ! [ i ]
if ( isString ( child ) ) {
context . push ( child )
context . push ( child , NewlineType . Unknown )
} else {
genNode ( child , context )
}
@ -715,9 +770,9 @@ function genExpressionAsPropertyKey(
const text = isSimpleIdentifier ( node . content )
? node . content
: JSON . stringify ( node . content )
push ( text , node )
push ( text , NewlineType . None , node )
} else {
push ( ` [ ${ node . content } ] ` , node )
push ( ` [ ${ node . content } ] ` , NewlineType . Unknown , node )
}
}
@ -726,7 +781,11 @@ function genComment(node: CommentNode, context: CodegenContext) {
if ( pure ) {
push ( PURE_ANNOTATION )
}
push ( ` ${ helper ( CREATE_COMMENT ) } ( ${ JSON . stringify ( node . content ) } ) ` , node )
push (
` ${ helper ( CREATE_COMMENT ) } ( ${ JSON . stringify ( node . content ) } ) ` ,
NewlineType . Unknown ,
node
)
}
function genVNodeCall ( node : VNodeCall , context : CodegenContext ) {
@ -754,7 +813,7 @@ function genVNodeCall(node: VNodeCall, context: CodegenContext) {
const callHelper : symbol = isBlock
? getVNodeBlockHelper ( context . inSSR , isComponent )
: getVNodeHelper ( context . inSSR , isComponent )
push ( helper ( callHelper ) + ` ( ` , node )
push ( helper ( callHelper ) + ` ( ` , NewlineType . None , node )
genNodeList (
genNullableArgs ( [ tag , props , children , patchFlag , dynamicProps ] ) ,
context
@ -785,7 +844,7 @@ function genCallExpression(node: CallExpression, context: CodegenContext) {
if ( pure ) {
push ( PURE_ANNOTATION )
}
push ( callee + ` ( ` , node )
push ( callee + ` ( ` , NewlineType . None , node )
genNodeList ( node . arguments , context )
push ( ` ) ` )
}
@ -794,7 +853,7 @@ function genObjectExpression(node: ObjectExpression, context: CodegenContext) {
const { push , indent , deindent , newline } = context
const { properties } = node
if ( ! properties . length ) {
push ( ` {} ` , node )
push ( ` {} ` , NewlineType . None , node )
return
}
const multilines =
@ -834,7 +893,7 @@ function genFunctionExpression(
// wrap slot functions with owner context
push ( ` _ ${ helperNameMap [ WITH_CTX ] } ( ` )
}
push ( ` ( ` , node )
push ( ` ( ` , NewlineType . None , node )
if ( isArray ( params ) ) {
genNodeList ( params , context )
} else if ( params ) {
@ -934,7 +993,7 @@ function genTemplateLiteral(node: TemplateLiteral, context: CodegenContext) {
for ( let i = 0 ; i < l ; i ++ ) {
const e = node . elements [ i ]
if ( isString ( e ) ) {
push ( e . replace ( /(`|\$|\\)/g , '\\$1' ) )
push ( e . replace ( /(`|\$|\\)/g , '\\$1' ) , NewlineType . Unknown )
} else {
push ( '${' )
if ( multilines ) indent ( )