2019-12-11 01:53:26 +08:00
|
|
|
import { CodegenOptions } from './options'
|
2019-09-20 11:05:51 +08:00
|
|
|
import {
|
|
|
|
RootNode,
|
2019-10-01 21:27:34 +08:00
|
|
|
TemplateChildNode,
|
2019-09-20 11:05:51 +08:00
|
|
|
TextNode,
|
|
|
|
CommentNode,
|
|
|
|
ExpressionNode,
|
2019-09-23 04:50:57 +08:00
|
|
|
NodeTypes,
|
|
|
|
JSChildNode,
|
|
|
|
CallExpression,
|
|
|
|
ArrayExpression,
|
|
|
|
ObjectExpression,
|
2019-09-27 23:42:02 +08:00
|
|
|
Position,
|
|
|
|
InterpolationNode,
|
|
|
|
CompoundExpressionNode,
|
2019-09-28 08:29:20 +08:00
|
|
|
SimpleExpressionNode,
|
2019-10-02 04:48:20 +08:00
|
|
|
FunctionExpression,
|
2019-10-19 09:51:34 +08:00
|
|
|
ConditionalExpression,
|
2019-12-14 01:56:25 +08:00
|
|
|
CacheExpression,
|
2020-02-02 13:05:27 +08:00
|
|
|
locStub,
|
|
|
|
SSRCodegenNode,
|
2020-02-03 10:35:28 +08:00
|
|
|
TemplateLiteral,
|
2020-02-05 11:49:47 +08:00
|
|
|
IfStatement,
|
2020-02-07 14:06:51 +08:00
|
|
|
AssignmentExpression,
|
2020-02-12 07:12:56 +08:00
|
|
|
ReturnStatement,
|
2020-03-17 06:14:49 +08:00
|
|
|
VNodeCall,
|
|
|
|
SequenceExpression
|
2019-09-20 11:05:51 +08:00
|
|
|
} from './ast'
|
|
|
|
import { SourceMapGenerator, RawSourceMap } from 'source-map'
|
2019-09-25 10:39:20 +08:00
|
|
|
import {
|
|
|
|
advancePositionWithMutation,
|
|
|
|
assert,
|
2021-06-23 07:15:20 +08:00
|
|
|
getVNodeBlockHelper,
|
|
|
|
getVNodeHelper,
|
2019-10-05 05:43:20 +08:00
|
|
|
isSimpleIdentifier,
|
2019-10-06 05:18:25 +08:00
|
|
|
toValidAssetId
|
2019-09-25 10:39:20 +08:00
|
|
|
} from './utils'
|
2019-10-06 05:18:25 +08:00
|
|
|
import { isString, isArray, isSymbol } from '@vue/shared'
|
|
|
|
import {
|
2019-10-25 05:55:00 +08:00
|
|
|
helperNameMap,
|
2020-01-27 06:35:21 +08:00
|
|
|
TO_DISPLAY_STRING,
|
2019-10-06 05:18:25 +08:00
|
|
|
CREATE_VNODE,
|
|
|
|
RESOLVE_COMPONENT,
|
2019-10-24 05:57:40 +08:00
|
|
|
RESOLVE_DIRECTIVE,
|
2019-10-25 05:55:00 +08:00
|
|
|
SET_BLOCK_TRACKING,
|
2019-11-18 10:26:25 +08:00
|
|
|
CREATE_COMMENT,
|
2019-12-17 01:11:51 +08:00
|
|
|
CREATE_TEXT,
|
2021-03-27 22:53:45 +08:00
|
|
|
PUSH_SCOPE_ID,
|
|
|
|
POP_SCOPE_ID,
|
2020-02-12 07:12:56 +08:00
|
|
|
WITH_DIRECTIVES,
|
2021-06-23 07:15:20 +08:00
|
|
|
CREATE_ELEMENT_VNODE,
|
2020-02-13 00:56:42 +08:00
|
|
|
OPEN_BLOCK,
|
2020-03-17 01:06:37 +08:00
|
|
|
CREATE_STATIC,
|
2021-04-20 00:08:26 +08:00
|
|
|
WITH_CTX,
|
|
|
|
RESOLVE_FILTER
|
2019-10-06 05:18:25 +08:00
|
|
|
} from './runtimeHelpers'
|
2019-12-11 01:53:26 +08:00
|
|
|
import { ImportItem } from './transform'
|
2019-09-23 04:50:57 +08:00
|
|
|
|
2020-05-02 06:36:34 +08:00
|
|
|
const PURE_ANNOTATION = `/*#__PURE__*/`
|
|
|
|
|
2020-02-02 13:05:27 +08:00
|
|
|
type CodegenNode = TemplateChildNode | JSChildNode | SSRCodegenNode
|
2019-09-20 11:05:51 +08:00
|
|
|
|
|
|
|
export interface CodegenResult {
|
|
|
|
code: string
|
2020-11-11 05:28:34 +08:00
|
|
|
preamble: string
|
2019-09-25 21:23:34 +08:00
|
|
|
ast: RootNode
|
2019-09-20 11:05:51 +08:00
|
|
|
map?: RawSourceMap
|
|
|
|
}
|
|
|
|
|
2020-07-11 10:12:25 +08:00
|
|
|
export interface CodegenContext
|
2021-07-02 19:59:47 +08:00
|
|
|
extends Omit<Required<CodegenOptions>, 'bindingMetadata' | 'inline'> {
|
2019-09-20 11:05:51 +08:00
|
|
|
source: string
|
|
|
|
code: string
|
|
|
|
line: number
|
|
|
|
column: number
|
|
|
|
offset: number
|
2019-09-23 04:50:57 +08:00
|
|
|
indentLevel: number
|
2020-05-02 06:36:34 +08:00
|
|
|
pure: boolean
|
2019-09-20 11:05:51 +08:00
|
|
|
map?: SourceMapGenerator
|
2019-10-11 06:02:51 +08:00
|
|
|
helper(key: symbol): string
|
2019-12-14 01:56:25 +08:00
|
|
|
push(code: string, node?: CodegenNode): void
|
2019-09-23 04:50:57 +08:00
|
|
|
indent(): void
|
2019-09-23 05:06:46 +08:00
|
|
|
deindent(withoutNewLine?: boolean): void
|
2019-09-23 04:50:57 +08:00
|
|
|
newline(): void
|
2019-09-20 11:05:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function createCodegenContext(
|
|
|
|
ast: RootNode,
|
2019-09-23 04:50:57 +08:00
|
|
|
{
|
|
|
|
mode = 'function',
|
2020-02-04 06:47:06 +08:00
|
|
|
prefixIdentifiers = mode === 'module',
|
2019-09-25 03:49:02 +08:00
|
|
|
sourceMap = false,
|
2019-12-17 01:11:51 +08:00
|
|
|
filename = `template.vue.html`,
|
2020-02-02 13:05:27 +08:00
|
|
|
scopeId = null,
|
2020-07-11 08:43:52 +08:00
|
|
|
optimizeImports = false,
|
2020-02-07 04:29:02 +08:00
|
|
|
runtimeGlobalName = `Vue`,
|
|
|
|
runtimeModuleName = `vue`,
|
2021-09-23 22:08:28 +08:00
|
|
|
ssrRuntimeModuleName = 'vue/server-renderer',
|
2021-07-02 19:59:47 +08:00
|
|
|
ssr = false,
|
2021-06-23 07:15:20 +08:00
|
|
|
isTS = false,
|
|
|
|
inSSR = false
|
2019-09-23 04:50:57 +08:00
|
|
|
}: CodegenOptions
|
2019-09-20 11:05:51 +08:00
|
|
|
): CodegenContext {
|
|
|
|
const context: CodegenContext = {
|
2019-09-23 04:50:57 +08:00
|
|
|
mode,
|
2019-09-24 01:29:41 +08:00
|
|
|
prefixIdentifiers,
|
2019-09-25 03:49:02 +08:00
|
|
|
sourceMap,
|
2019-09-21 00:16:19 +08:00
|
|
|
filename,
|
2019-12-17 01:11:51 +08:00
|
|
|
scopeId,
|
2020-07-11 08:43:52 +08:00
|
|
|
optimizeImports,
|
2020-02-07 04:29:02 +08:00
|
|
|
runtimeGlobalName,
|
|
|
|
runtimeModuleName,
|
2021-09-23 22:08:28 +08:00
|
|
|
ssrRuntimeModuleName,
|
2020-02-02 13:05:27 +08:00
|
|
|
ssr,
|
2021-07-02 19:59:47 +08:00
|
|
|
isTS,
|
2021-06-23 07:15:20 +08:00
|
|
|
inSSR,
|
2019-09-20 11:05:51 +08:00
|
|
|
source: ast.loc.source,
|
|
|
|
code: ``,
|
|
|
|
column: 1,
|
|
|
|
line: 1,
|
|
|
|
offset: 0,
|
2019-09-23 04:50:57 +08:00
|
|
|
indentLevel: 0,
|
2020-05-02 06:36:34 +08:00
|
|
|
pure: false,
|
2019-12-14 02:22:15 +08:00
|
|
|
map: undefined,
|
2019-10-06 05:18:25 +08:00
|
|
|
helper(key) {
|
2020-02-08 07:53:39 +08:00
|
|
|
return `_${helperNameMap[key]}`
|
2019-09-26 07:17:45 +08:00
|
|
|
},
|
2019-12-14 01:56:25 +08:00
|
|
|
push(code, node) {
|
2019-09-23 04:50:57 +08:00
|
|
|
context.code += code
|
2019-09-27 21:25:52 +08:00
|
|
|
if (!__BROWSER__ && context.map) {
|
2019-09-20 11:05:51 +08:00
|
|
|
if (node) {
|
2019-09-26 22:13:46 +08:00
|
|
|
let name
|
2019-09-27 23:42:02 +08:00
|
|
|
if (node.type === NodeTypes.SIMPLE_EXPRESSION && !node.isStatic) {
|
2019-09-26 22:13:46 +08:00
|
|
|
const content = node.content.replace(/^_ctx\./, '')
|
|
|
|
if (content !== node.content && isSimpleIdentifier(content)) {
|
|
|
|
name = content
|
|
|
|
}
|
|
|
|
}
|
2019-09-27 21:25:52 +08:00
|
|
|
addMapping(node.loc.start, name)
|
2019-09-20 11:05:51 +08:00
|
|
|
}
|
2019-09-27 21:25:52 +08:00
|
|
|
advancePositionWithMutation(context, code)
|
2019-12-14 01:56:25 +08:00
|
|
|
if (node && node.loc !== locStub) {
|
2019-09-27 21:25:52 +08:00
|
|
|
addMapping(node.loc.end)
|
2019-09-27 02:55:53 +08:00
|
|
|
}
|
2019-09-20 11:05:51 +08:00
|
|
|
}
|
2019-09-23 04:50:57 +08:00
|
|
|
},
|
|
|
|
indent() {
|
|
|
|
newline(++context.indentLevel)
|
|
|
|
},
|
2019-09-23 05:06:46 +08:00
|
|
|
deindent(withoutNewLine = false) {
|
|
|
|
if (withoutNewLine) {
|
|
|
|
--context.indentLevel
|
|
|
|
} else {
|
|
|
|
newline(--context.indentLevel)
|
|
|
|
}
|
2019-09-23 04:50:57 +08:00
|
|
|
},
|
|
|
|
newline() {
|
|
|
|
newline(context.indentLevel)
|
2019-09-20 11:05:51 +08:00
|
|
|
}
|
|
|
|
}
|
2019-09-27 21:25:52 +08:00
|
|
|
|
|
|
|
function newline(n: number) {
|
|
|
|
context.push('\n' + ` `.repeat(n))
|
|
|
|
}
|
|
|
|
|
|
|
|
function addMapping(loc: Position, name?: string) {
|
|
|
|
context.map!.addMapping({
|
|
|
|
name,
|
|
|
|
source: context.filename,
|
|
|
|
original: {
|
|
|
|
line: loc.line,
|
|
|
|
column: loc.column - 1 // source-map column is 0 based
|
|
|
|
},
|
|
|
|
generated: {
|
|
|
|
line: context.line,
|
|
|
|
column: context.column - 1
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-12-14 02:22:15 +08:00
|
|
|
if (!__BROWSER__ && sourceMap) {
|
|
|
|
// lazy require source-map implementation, only in non-browser builds
|
2020-04-26 13:24:25 +08:00
|
|
|
context.map = new SourceMapGenerator()
|
2019-12-14 02:22:15 +08:00
|
|
|
context.map!.setSourceContent(filename, context.source)
|
2019-09-21 00:19:52 +08:00
|
|
|
}
|
2019-12-14 02:22:15 +08:00
|
|
|
|
2019-09-20 11:05:51 +08:00
|
|
|
return context
|
|
|
|
}
|
|
|
|
|
2019-09-23 04:50:57 +08:00
|
|
|
export function generate(
|
|
|
|
ast: RootNode,
|
2020-07-22 03:16:20 +08:00
|
|
|
options: CodegenOptions & {
|
|
|
|
onContextCreated?: (context: CodegenContext) => void
|
|
|
|
} = {}
|
2019-09-23 04:50:57 +08:00
|
|
|
): CodegenResult {
|
|
|
|
const context = createCodegenContext(ast, options)
|
2020-07-22 03:16:20 +08:00
|
|
|
if (options.onContextCreated) options.onContextCreated(context)
|
2019-10-06 05:18:25 +08:00
|
|
|
const {
|
|
|
|
mode,
|
|
|
|
push,
|
|
|
|
prefixIdentifiers,
|
|
|
|
indent,
|
|
|
|
deindent,
|
2019-12-17 01:11:51 +08:00
|
|
|
newline,
|
2021-03-27 22:53:45 +08:00
|
|
|
scopeId,
|
2020-02-02 13:05:27 +08:00
|
|
|
ssr
|
2019-10-06 05:18:25 +08:00
|
|
|
} = context
|
2021-03-06 00:10:06 +08:00
|
|
|
|
2019-10-06 05:18:25 +08:00
|
|
|
const hasHelpers = ast.helpers.length > 0
|
2019-09-26 10:29:37 +08:00
|
|
|
const useWithBlock = !prefixIdentifiers && mode !== 'module'
|
2021-03-27 22:53:45 +08:00
|
|
|
const genScopeId = !__BROWSER__ && scopeId != null && mode === 'module'
|
2020-11-21 03:22:51 +08:00
|
|
|
const isSetupInlined = !__BROWSER__ && !!options.inline
|
2019-09-26 03:09:58 +08:00
|
|
|
|
|
|
|
// preambles
|
2020-11-11 05:28:34 +08:00
|
|
|
// in setup() inline mode, the preamble is generated in a sub context
|
|
|
|
// and returned separately.
|
|
|
|
const preambleContext = isSetupInlined
|
|
|
|
? createCodegenContext(ast, options)
|
|
|
|
: context
|
2020-02-08 07:00:30 +08:00
|
|
|
if (!__BROWSER__ && mode === 'module') {
|
2021-03-27 22:53:45 +08:00
|
|
|
genModulePreamble(ast, preambleContext, genScopeId, isSetupInlined)
|
2019-09-23 04:50:57 +08:00
|
|
|
} else {
|
2020-11-11 05:28:34 +08:00
|
|
|
genFunctionPreamble(ast, preambleContext)
|
2019-09-23 04:50:57 +08:00
|
|
|
}
|
2020-11-21 03:22:51 +08:00
|
|
|
// enter render function
|
|
|
|
const functionName = ssr ? `ssrRender` : `render`
|
2020-11-19 10:16:09 +08:00
|
|
|
const args = ssr ? ['_ctx', '_push', '_parent', '_attrs'] : ['_ctx', '_cache']
|
|
|
|
if (!__BROWSER__ && options.bindingMetadata && !options.inline) {
|
|
|
|
// binding optimization args
|
|
|
|
args.push('$props', '$setup', '$data', '$options')
|
|
|
|
}
|
|
|
|
const signature =
|
|
|
|
!__BROWSER__ && options.isTS
|
|
|
|
? args.map(arg => `${arg}: any`).join(',')
|
2020-11-19 11:34:55 +08:00
|
|
|
: args.join(', ')
|
2020-11-21 03:22:51 +08:00
|
|
|
|
2021-07-14 22:04:35 +08:00
|
|
|
if (isSetupInlined) {
|
2020-11-21 03:22:51 +08:00
|
|
|
push(`(${signature}) => {`)
|
2020-02-02 13:05:27 +08:00
|
|
|
} else {
|
2020-11-21 03:22:51 +08:00
|
|
|
push(`function ${functionName}(${signature}) {`)
|
2020-02-02 13:05:27 +08:00
|
|
|
}
|
2019-09-23 14:52:54 +08:00
|
|
|
indent()
|
2019-09-26 03:09:58 +08:00
|
|
|
|
2019-09-26 10:29:37 +08:00
|
|
|
if (useWithBlock) {
|
2020-02-14 07:28:40 +08:00
|
|
|
push(`with (_ctx) {`)
|
2019-09-26 03:09:58 +08:00
|
|
|
indent()
|
|
|
|
// function mode const declarations should be inside with block
|
2019-09-26 07:17:45 +08:00
|
|
|
// also they should be renamed to avoid collision with user properties
|
2019-10-06 05:18:25 +08:00
|
|
|
if (hasHelpers) {
|
|
|
|
push(
|
|
|
|
`const { ${ast.helpers
|
|
|
|
.map(s => `${helperNameMap[s]}: _${helperNameMap[s]}`)
|
|
|
|
.join(', ')} } = _Vue`
|
|
|
|
)
|
2020-02-10 22:33:04 +08:00
|
|
|
push(`\n`)
|
2019-10-02 00:25:13 +08:00
|
|
|
newline()
|
2019-09-26 03:09:58 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-23 11:07:36 +08:00
|
|
|
// generate asset resolution statements
|
2019-10-06 05:18:25 +08:00
|
|
|
if (ast.components.length) {
|
|
|
|
genAssets(ast.components, 'component', context)
|
2020-02-10 22:33:04 +08:00
|
|
|
if (ast.directives.length || ast.temps > 0) {
|
|
|
|
newline()
|
|
|
|
}
|
2019-10-06 05:18:25 +08:00
|
|
|
}
|
|
|
|
if (ast.directives.length) {
|
|
|
|
genAssets(ast.directives, 'directive', context)
|
2020-02-10 22:33:04 +08:00
|
|
|
if (ast.temps > 0) {
|
|
|
|
newline()
|
|
|
|
}
|
2019-10-06 05:18:25 +08:00
|
|
|
}
|
2021-04-20 00:08:26 +08:00
|
|
|
if (__COMPAT__ && ast.filters && ast.filters.length) {
|
|
|
|
newline()
|
|
|
|
genAssets(ast.filters, 'filter', context)
|
|
|
|
newline()
|
|
|
|
}
|
|
|
|
|
2020-02-05 11:49:47 +08:00
|
|
|
if (ast.temps > 0) {
|
|
|
|
push(`let `)
|
|
|
|
for (let i = 0; i < ast.temps; i++) {
|
|
|
|
push(`${i > 0 ? `, ` : ``}_temp${i}`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ast.components.length || ast.directives.length || ast.temps) {
|
2020-02-10 22:33:04 +08:00
|
|
|
push(`\n`)
|
2019-09-23 14:52:54 +08:00
|
|
|
newline()
|
|
|
|
}
|
2019-09-26 03:09:58 +08:00
|
|
|
|
|
|
|
// generate the VNode tree expression
|
2020-02-02 13:05:27 +08:00
|
|
|
if (!ssr) {
|
|
|
|
push(`return `)
|
|
|
|
}
|
2019-10-03 01:11:07 +08:00
|
|
|
if (ast.codegenNode) {
|
|
|
|
genNode(ast.codegenNode, context)
|
|
|
|
} else {
|
|
|
|
push(`null`)
|
|
|
|
}
|
2019-09-26 10:29:37 +08:00
|
|
|
|
|
|
|
if (useWithBlock) {
|
2019-09-23 04:50:57 +08:00
|
|
|
deindent()
|
|
|
|
push(`}`)
|
|
|
|
}
|
2019-09-26 10:29:37 +08:00
|
|
|
|
2019-09-23 04:50:57 +08:00
|
|
|
deindent()
|
|
|
|
push(`}`)
|
2019-12-17 01:11:51 +08:00
|
|
|
|
2019-09-23 04:50:57 +08:00
|
|
|
return {
|
2019-09-25 21:23:34 +08:00
|
|
|
ast,
|
2019-09-23 04:50:57 +08:00
|
|
|
code: context.code,
|
2020-11-11 05:28:34 +08:00
|
|
|
preamble: isSetupInlined ? preambleContext.code : ``,
|
2019-12-14 01:56:25 +08:00
|
|
|
// SourceMapGenerator does have toJSON() method but it's not in the types
|
|
|
|
map: context.map ? (context.map as any).toJSON() : undefined
|
2019-09-23 04:50:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-04 06:47:06 +08:00
|
|
|
function genFunctionPreamble(ast: RootNode, context: CodegenContext) {
|
2020-02-07 04:29:02 +08:00
|
|
|
const {
|
|
|
|
ssr,
|
|
|
|
prefixIdentifiers,
|
|
|
|
push,
|
|
|
|
newline,
|
|
|
|
runtimeModuleName,
|
2021-09-23 22:08:28 +08:00
|
|
|
runtimeGlobalName,
|
|
|
|
ssrRuntimeModuleName
|
2020-02-07 04:29:02 +08:00
|
|
|
} = context
|
2020-02-08 07:00:30 +08:00
|
|
|
const VueBinding =
|
|
|
|
!__BROWSER__ && ssr
|
|
|
|
? `require(${JSON.stringify(runtimeModuleName)})`
|
|
|
|
: runtimeGlobalName
|
2020-02-08 07:53:39 +08:00
|
|
|
const aliasHelper = (s: symbol) => `${helperNameMap[s]}: _${helperNameMap[s]}`
|
2020-02-04 06:47:06 +08:00
|
|
|
// Generate const declaration for helpers
|
|
|
|
// In prefix mode, we place the const declaration at top so it's done
|
|
|
|
// only once; But if we not prefixing, we place the declaration inside the
|
|
|
|
// with block so it doesn't incur the `in` check cost for every helper access.
|
|
|
|
if (ast.helpers.length > 0) {
|
2020-02-08 07:00:30 +08:00
|
|
|
if (!__BROWSER__ && prefixIdentifiers) {
|
2020-02-08 07:53:39 +08:00
|
|
|
push(
|
|
|
|
`const { ${ast.helpers.map(aliasHelper).join(', ')} } = ${VueBinding}\n`
|
|
|
|
)
|
2020-02-04 06:47:06 +08:00
|
|
|
} else {
|
|
|
|
// "with" mode.
|
|
|
|
// save Vue in a separate variable to avoid collision
|
|
|
|
push(`const _Vue = ${VueBinding}\n`)
|
|
|
|
// 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.
|
|
|
|
if (ast.hoists.length) {
|
2020-02-13 00:56:42 +08:00
|
|
|
const staticHelpers = [
|
|
|
|
CREATE_VNODE,
|
2021-06-23 07:15:20 +08:00
|
|
|
CREATE_ELEMENT_VNODE,
|
2020-02-13 00:56:42 +08:00
|
|
|
CREATE_COMMENT,
|
|
|
|
CREATE_TEXT,
|
|
|
|
CREATE_STATIC
|
|
|
|
]
|
2020-02-04 06:47:06 +08:00
|
|
|
.filter(helper => ast.helpers.includes(helper))
|
2020-02-08 07:53:39 +08:00
|
|
|
.map(aliasHelper)
|
2020-02-04 06:47:06 +08:00
|
|
|
.join(', ')
|
|
|
|
push(`const { ${staticHelpers} } = _Vue\n`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// generate variables for ssr helpers
|
|
|
|
if (!__BROWSER__ && ast.ssrHelpers && ast.ssrHelpers.length) {
|
2020-07-08 18:32:42 +08:00
|
|
|
// ssr guarantees prefixIdentifier: true
|
2020-02-04 06:47:06 +08:00
|
|
|
push(
|
|
|
|
`const { ${ast.ssrHelpers
|
2020-02-08 07:53:39 +08:00
|
|
|
.map(aliasHelper)
|
2021-09-23 22:08:28 +08:00
|
|
|
.join(', ')} } = require("${ssrRuntimeModuleName}")\n`
|
2020-02-04 06:47:06 +08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
genHoists(ast.hoists, context)
|
|
|
|
newline()
|
|
|
|
push(`return `)
|
|
|
|
}
|
|
|
|
|
|
|
|
function genModulePreamble(
|
|
|
|
ast: RootNode,
|
|
|
|
context: CodegenContext,
|
2021-03-27 22:53:45 +08:00
|
|
|
genScopeId: boolean,
|
2020-11-11 05:28:34 +08:00
|
|
|
inline?: boolean
|
2020-02-04 06:47:06 +08:00
|
|
|
) {
|
2021-09-23 22:08:28 +08:00
|
|
|
const {
|
|
|
|
push,
|
|
|
|
newline,
|
|
|
|
optimizeImports,
|
|
|
|
runtimeModuleName,
|
|
|
|
ssrRuntimeModuleName
|
|
|
|
} = context
|
2020-02-07 05:51:26 +08:00
|
|
|
|
2021-09-01 23:30:34 +08:00
|
|
|
if (genScopeId && ast.hoists.length) {
|
|
|
|
ast.helpers.push(PUSH_SCOPE_ID, POP_SCOPE_ID)
|
2020-02-04 06:47:06 +08:00
|
|
|
}
|
2020-02-07 05:51:26 +08:00
|
|
|
|
|
|
|
// generate import statements for helpers
|
2020-02-04 06:47:06 +08:00
|
|
|
if (ast.helpers.length) {
|
2020-07-11 08:43:52 +08:00
|
|
|
if (optimizeImports) {
|
2020-02-08 07:53:39 +08:00
|
|
|
// when bundled with webpack with code-split, calling an import binding
|
|
|
|
// as a function leads to it being wrapped with `Object(a.b)` or `(0,a.b)`,
|
|
|
|
// incurring both payload size increase and potential perf overhead.
|
2020-07-08 18:32:42 +08:00
|
|
|
// therefore we assign the imports to variables (which is a constant ~50b
|
2020-02-08 07:53:39 +08:00
|
|
|
// cost per-component instead of scaling with template size)
|
|
|
|
push(
|
|
|
|
`import { ${ast.helpers
|
|
|
|
.map(s => helperNameMap[s])
|
|
|
|
.join(', ')} } from ${JSON.stringify(runtimeModuleName)}\n`
|
|
|
|
)
|
|
|
|
push(
|
|
|
|
`\n// Binding optimization for webpack code-split\nconst ${ast.helpers
|
|
|
|
.map(s => `_${helperNameMap[s]} = ${helperNameMap[s]}`)
|
|
|
|
.join(', ')}\n`
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
push(
|
|
|
|
`import { ${ast.helpers
|
|
|
|
.map(s => `${helperNameMap[s]} as _${helperNameMap[s]}`)
|
|
|
|
.join(', ')} } from ${JSON.stringify(runtimeModuleName)}\n`
|
|
|
|
)
|
|
|
|
}
|
2020-02-04 06:47:06 +08:00
|
|
|
}
|
2020-02-07 05:51:26 +08:00
|
|
|
|
2020-02-08 07:00:30 +08:00
|
|
|
if (ast.ssrHelpers && ast.ssrHelpers.length) {
|
|
|
|
push(
|
|
|
|
`import { ${ast.ssrHelpers
|
2020-02-08 07:53:39 +08:00
|
|
|
.map(s => `${helperNameMap[s]} as _${helperNameMap[s]}`)
|
2021-09-23 22:08:28 +08:00
|
|
|
.join(', ')} } from "${ssrRuntimeModuleName}"\n`
|
2020-02-08 07:00:30 +08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ast.imports.length) {
|
|
|
|
genImports(ast.imports, context)
|
|
|
|
newline()
|
|
|
|
}
|
|
|
|
|
2020-02-04 06:47:06 +08:00
|
|
|
genHoists(ast.hoists, context)
|
|
|
|
newline()
|
2020-11-11 05:28:34 +08:00
|
|
|
|
|
|
|
if (!inline) {
|
|
|
|
push(`export `)
|
|
|
|
}
|
2020-02-04 06:47:06 +08:00
|
|
|
}
|
|
|
|
|
2019-10-06 05:18:25 +08:00
|
|
|
function genAssets(
|
|
|
|
assets: string[],
|
2021-04-20 00:08:26 +08:00
|
|
|
type: 'component' | 'directive' | 'filter',
|
2021-07-02 19:59:47 +08:00
|
|
|
{ helper, push, newline, isTS }: CodegenContext
|
2019-10-06 05:18:25 +08:00
|
|
|
) {
|
2020-02-06 12:07:23 +08:00
|
|
|
const resolver = helper(
|
2021-04-20 00:08:26 +08:00
|
|
|
__COMPAT__ && type === 'filter'
|
|
|
|
? RESOLVE_FILTER
|
|
|
|
: type === 'component'
|
2021-07-20 06:24:18 +08:00
|
|
|
? RESOLVE_COMPONENT
|
|
|
|
: RESOLVE_DIRECTIVE
|
2019-10-06 05:18:25 +08:00
|
|
|
)
|
|
|
|
for (let i = 0; i < assets.length; i++) {
|
2021-03-26 22:04:29 +08:00
|
|
|
let id = assets[i]
|
|
|
|
// potential component implicit self-reference inferred from SFC filename
|
|
|
|
const maybeSelfReference = id.endsWith('__self')
|
|
|
|
if (maybeSelfReference) {
|
|
|
|
id = id.slice(0, -6)
|
|
|
|
}
|
2020-02-06 12:07:23 +08:00
|
|
|
push(
|
2021-03-26 22:04:29 +08:00
|
|
|
`const ${toValidAssetId(id, type)} = ${resolver}(${JSON.stringify(id)}${
|
|
|
|
maybeSelfReference ? `, true` : ``
|
2021-07-02 19:59:47 +08:00
|
|
|
})${isTS ? `!` : ``}`
|
2019-10-06 05:18:25 +08:00
|
|
|
)
|
2020-02-06 12:07:23 +08:00
|
|
|
if (i < assets.length - 1) {
|
|
|
|
newline()
|
|
|
|
}
|
2019-10-06 05:18:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-16 03:50:42 +08:00
|
|
|
function genHoists(hoists: (JSChildNode | null)[], context: CodegenContext) {
|
2019-10-05 02:34:26 +08:00
|
|
|
if (!hoists.length) {
|
|
|
|
return
|
|
|
|
}
|
2020-05-02 06:36:34 +08:00
|
|
|
context.pure = true
|
2019-12-17 01:11:51 +08:00
|
|
|
const { push, newline, helper, scopeId, mode } = context
|
2020-02-02 13:05:27 +08:00
|
|
|
const genScopeId = !__BROWSER__ && scopeId != null && mode !== 'function'
|
2019-12-17 01:11:51 +08:00
|
|
|
newline()
|
|
|
|
|
2021-09-20 05:14:26 +08:00
|
|
|
// generate inlined withScopeId helper
|
2019-12-17 01:11:51 +08:00
|
|
|
if (genScopeId) {
|
2021-09-20 05:14:26 +08:00
|
|
|
push(
|
|
|
|
`const _withScopeId = n => (${helper(
|
|
|
|
PUSH_SCOPE_ID
|
|
|
|
)}("${scopeId}"),n=n(),${helper(POP_SCOPE_ID)}(),n)`
|
|
|
|
)
|
2019-12-17 01:11:51 +08:00
|
|
|
newline()
|
|
|
|
}
|
|
|
|
|
2021-09-20 05:14:26 +08:00
|
|
|
for (let i = 0; i < hoists.length; i++) {
|
|
|
|
const exp = hoists[i]
|
2020-05-16 03:50:42 +08:00
|
|
|
if (exp) {
|
2021-09-20 05:14:26 +08:00
|
|
|
const needScopeIdWrapper = genScopeId && exp.type === NodeTypes.VNODE_CALL
|
|
|
|
push(
|
|
|
|
`const _hoisted_${i + 1} = ${
|
|
|
|
needScopeIdWrapper ? `${PURE_ANNOTATION} _withScopeId(() => ` : ``
|
|
|
|
}`
|
|
|
|
)
|
2020-05-16 03:50:42 +08:00
|
|
|
genNode(exp, context)
|
2021-09-20 05:14:26 +08:00
|
|
|
if (needScopeIdWrapper) {
|
|
|
|
push(`)`)
|
|
|
|
}
|
2020-05-16 03:50:42 +08:00
|
|
|
newline()
|
|
|
|
}
|
2019-12-17 01:11:51 +08:00
|
|
|
}
|
2021-09-20 05:14:26 +08:00
|
|
|
|
2020-05-02 06:36:34 +08:00
|
|
|
context.pure = false
|
2019-09-26 02:13:33 +08:00
|
|
|
}
|
|
|
|
|
2019-12-11 01:53:26 +08:00
|
|
|
function genImports(importsOptions: ImportItem[], context: CodegenContext) {
|
2019-12-02 01:02:53 +08:00
|
|
|
if (!importsOptions.length) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
importsOptions.forEach(imports => {
|
|
|
|
context.push(`import `)
|
|
|
|
genNode(imports.exp, context)
|
|
|
|
context.push(` from '${imports.path}'`)
|
|
|
|
context.newline()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-10-03 11:10:41 +08:00
|
|
|
function isText(n: string | CodegenNode) {
|
|
|
|
return (
|
|
|
|
isString(n) ||
|
|
|
|
n.type === NodeTypes.SIMPLE_EXPRESSION ||
|
|
|
|
n.type === NodeTypes.TEXT ||
|
|
|
|
n.type === NodeTypes.INTERPOLATION ||
|
|
|
|
n.type === NodeTypes.COMPOUND_EXPRESSION
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-09-23 04:50:57 +08:00
|
|
|
function genNodeListAsArray(
|
2019-10-01 21:27:34 +08:00
|
|
|
nodes: (string | CodegenNode | TemplateChildNode[])[],
|
2019-09-23 04:50:57 +08:00
|
|
|
context: CodegenContext
|
|
|
|
) {
|
2019-09-28 10:49:20 +08:00
|
|
|
const multilines =
|
2019-10-02 03:04:58 +08:00
|
|
|
nodes.length > 3 ||
|
2019-10-03 11:10:41 +08:00
|
|
|
((!__BROWSER__ || __DEV__) && nodes.some(n => isArray(n) || !isText(n)))
|
2019-09-23 04:50:57 +08:00
|
|
|
context.push(`[`)
|
|
|
|
multilines && context.indent()
|
|
|
|
genNodeList(nodes, context, multilines)
|
|
|
|
multilines && context.deindent()
|
2019-09-20 11:05:51 +08:00
|
|
|
context.push(`]`)
|
|
|
|
}
|
|
|
|
|
2019-09-23 04:50:57 +08:00
|
|
|
function genNodeList(
|
2019-10-11 06:02:51 +08:00
|
|
|
nodes: (string | symbol | CodegenNode | TemplateChildNode[])[],
|
2019-09-23 04:50:57 +08:00
|
|
|
context: CodegenContext,
|
2020-02-04 06:56:10 +08:00
|
|
|
multilines: boolean = false,
|
|
|
|
comma: boolean = true
|
2019-09-23 04:50:57 +08:00
|
|
|
) {
|
|
|
|
const { push, newline } = context
|
|
|
|
for (let i = 0; i < nodes.length; i++) {
|
|
|
|
const node = nodes[i]
|
|
|
|
if (isString(node)) {
|
|
|
|
push(node)
|
|
|
|
} else if (isArray(node)) {
|
2019-10-02 11:53:52 +08:00
|
|
|
genNodeListAsArray(node, context)
|
2019-09-23 04:50:57 +08:00
|
|
|
} else {
|
|
|
|
genNode(node, context)
|
|
|
|
}
|
|
|
|
if (i < nodes.length - 1) {
|
2020-02-04 07:30:56 +08:00
|
|
|
if (multilines) {
|
|
|
|
comma && push(',')
|
|
|
|
newline()
|
|
|
|
} else {
|
|
|
|
comma && push(', ')
|
|
|
|
}
|
2019-09-23 04:50:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-11 06:02:51 +08:00
|
|
|
function genNode(node: CodegenNode | symbol | string, context: CodegenContext) {
|
2019-10-05 02:34:26 +08:00
|
|
|
if (isString(node)) {
|
|
|
|
context.push(node)
|
|
|
|
return
|
|
|
|
}
|
2019-10-06 05:18:25 +08:00
|
|
|
if (isSymbol(node)) {
|
|
|
|
context.push(context.helper(node))
|
|
|
|
return
|
|
|
|
}
|
2019-09-20 11:05:51 +08:00
|
|
|
switch (node.type) {
|
|
|
|
case NodeTypes.ELEMENT:
|
2019-10-02 04:48:20 +08:00
|
|
|
case NodeTypes.IF:
|
|
|
|
case NodeTypes.FOR:
|
|
|
|
__DEV__ &&
|
|
|
|
assert(
|
|
|
|
node.codegenNode != null,
|
|
|
|
`Codegen node is missing for element/if/for node. ` +
|
|
|
|
`Apply appropriate transforms first.`
|
|
|
|
)
|
|
|
|
genNode(node.codegenNode!, context)
|
2019-09-20 11:05:51 +08:00
|
|
|
break
|
|
|
|
case NodeTypes.TEXT:
|
|
|
|
genText(node, context)
|
|
|
|
break
|
2019-09-27 23:42:02 +08:00
|
|
|
case NodeTypes.SIMPLE_EXPRESSION:
|
2019-09-20 11:05:51 +08:00
|
|
|
genExpression(node, context)
|
|
|
|
break
|
2019-09-27 23:42:02 +08:00
|
|
|
case NodeTypes.INTERPOLATION:
|
|
|
|
genInterpolation(node, context)
|
|
|
|
break
|
2019-10-22 03:52:29 +08:00
|
|
|
case NodeTypes.TEXT_CALL:
|
|
|
|
genNode(node.codegenNode, context)
|
|
|
|
break
|
2019-09-27 23:42:02 +08:00
|
|
|
case NodeTypes.COMPOUND_EXPRESSION:
|
|
|
|
genCompoundExpression(node, context)
|
|
|
|
break
|
2019-09-20 11:05:51 +08:00
|
|
|
case NodeTypes.COMMENT:
|
|
|
|
genComment(node, context)
|
|
|
|
break
|
2020-02-12 07:12:56 +08:00
|
|
|
case NodeTypes.VNODE_CALL:
|
|
|
|
genVNodeCall(node, context)
|
|
|
|
break
|
|
|
|
|
2019-09-23 04:50:57 +08:00
|
|
|
case NodeTypes.JS_CALL_EXPRESSION:
|
|
|
|
genCallExpression(node, context)
|
|
|
|
break
|
|
|
|
case NodeTypes.JS_OBJECT_EXPRESSION:
|
|
|
|
genObjectExpression(node, context)
|
|
|
|
break
|
|
|
|
case NodeTypes.JS_ARRAY_EXPRESSION:
|
|
|
|
genArrayExpression(node, context)
|
2019-09-23 14:52:54 +08:00
|
|
|
break
|
2019-10-02 04:48:20 +08:00
|
|
|
case NodeTypes.JS_FUNCTION_EXPRESSION:
|
|
|
|
genFunctionExpression(node, context)
|
2019-09-28 10:25:32 +08:00
|
|
|
break
|
2019-10-01 21:27:34 +08:00
|
|
|
case NodeTypes.JS_CONDITIONAL_EXPRESSION:
|
|
|
|
genConditionalExpression(node, context)
|
|
|
|
break
|
2019-10-19 09:51:34 +08:00
|
|
|
case NodeTypes.JS_CACHE_EXPRESSION:
|
|
|
|
genCacheExpression(node, context)
|
|
|
|
break
|
2020-02-02 13:05:27 +08:00
|
|
|
case NodeTypes.JS_BLOCK_STATEMENT:
|
2021-07-10 09:41:44 +08:00
|
|
|
genNodeList(node.body, context, true, false)
|
2020-02-02 13:05:27 +08:00
|
|
|
break
|
2021-07-10 09:41:44 +08:00
|
|
|
|
|
|
|
// SSR only types
|
2020-02-02 13:05:27 +08:00
|
|
|
case NodeTypes.JS_TEMPLATE_LITERAL:
|
|
|
|
!__BROWSER__ && genTemplateLiteral(node, context)
|
|
|
|
break
|
|
|
|
case NodeTypes.JS_IF_STATEMENT:
|
2020-02-03 10:35:28 +08:00
|
|
|
!__BROWSER__ && genIfStatement(node, context)
|
2020-02-02 13:05:27 +08:00
|
|
|
break
|
2020-02-05 11:49:47 +08:00
|
|
|
case NodeTypes.JS_ASSIGNMENT_EXPRESSION:
|
|
|
|
!__BROWSER__ && genAssignmentExpression(node, context)
|
|
|
|
break
|
2020-03-17 06:14:49 +08:00
|
|
|
case NodeTypes.JS_SEQUENCE_EXPRESSION:
|
|
|
|
!__BROWSER__ && genSequenceExpression(node, context)
|
|
|
|
break
|
2020-02-07 14:06:51 +08:00
|
|
|
case NodeTypes.JS_RETURN_STATEMENT:
|
|
|
|
!__BROWSER__ && genReturnStatement(node, context)
|
|
|
|
break
|
2020-02-02 13:05:27 +08:00
|
|
|
|
2019-09-29 02:15:10 +08:00
|
|
|
/* istanbul ignore next */
|
2020-02-15 05:41:55 +08:00
|
|
|
case NodeTypes.IF_BRANCH:
|
|
|
|
// noop
|
|
|
|
break
|
2019-09-23 14:52:54 +08:00
|
|
|
default:
|
2019-09-28 10:25:32 +08:00
|
|
|
if (__DEV__) {
|
2019-09-23 14:52:54 +08:00
|
|
|
assert(false, `unhandled codegen node type: ${(node as any).type}`)
|
2019-09-28 10:25:32 +08:00
|
|
|
// make sure we exhaust all possible types
|
|
|
|
const exhaustiveCheck: never = node
|
|
|
|
return exhaustiveCheck
|
|
|
|
}
|
2019-09-20 11:05:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-27 23:42:02 +08:00
|
|
|
function genText(
|
|
|
|
node: TextNode | SimpleExpressionNode,
|
|
|
|
context: CodegenContext
|
|
|
|
) {
|
2019-09-20 11:05:51 +08:00
|
|
|
context.push(JSON.stringify(node.content), node)
|
|
|
|
}
|
|
|
|
|
2019-09-27 23:42:02 +08:00
|
|
|
function genExpression(node: SimpleExpressionNode, context: CodegenContext) {
|
|
|
|
const { content, isStatic } = node
|
|
|
|
context.push(isStatic ? JSON.stringify(content) : content, node)
|
|
|
|
}
|
|
|
|
|
|
|
|
function genInterpolation(node: InterpolationNode, context: CodegenContext) {
|
2020-05-02 06:36:34 +08:00
|
|
|
const { push, helper, pure } = context
|
|
|
|
if (pure) push(PURE_ANNOTATION)
|
2020-01-27 06:35:21 +08:00
|
|
|
push(`${helper(TO_DISPLAY_STRING)}(`)
|
2019-09-27 23:42:02 +08:00
|
|
|
genNode(node.content, context)
|
|
|
|
push(`)`)
|
|
|
|
}
|
|
|
|
|
|
|
|
function genCompoundExpression(
|
|
|
|
node: CompoundExpressionNode,
|
|
|
|
context: CodegenContext
|
|
|
|
) {
|
|
|
|
for (let i = 0; i < node.children!.length; i++) {
|
|
|
|
const child = node.children![i]
|
|
|
|
if (isString(child)) {
|
|
|
|
context.push(child)
|
|
|
|
} else {
|
2019-10-01 02:51:55 +08:00
|
|
|
genNode(child, context)
|
2019-09-27 23:42:02 +08:00
|
|
|
}
|
2019-09-23 14:52:54 +08:00
|
|
|
}
|
2019-09-23 04:50:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function genExpressionAsPropertyKey(
|
|
|
|
node: ExpressionNode,
|
|
|
|
context: CodegenContext
|
|
|
|
) {
|
2019-09-24 09:22:52 +08:00
|
|
|
const { push } = context
|
2019-09-27 23:42:02 +08:00
|
|
|
if (node.type === NodeTypes.COMPOUND_EXPRESSION) {
|
2019-09-24 09:22:52 +08:00
|
|
|
push(`[`)
|
|
|
|
genCompoundExpression(node, context)
|
|
|
|
push(`]`)
|
2019-09-27 23:42:02 +08:00
|
|
|
} else if (node.isStatic) {
|
2019-09-23 04:50:57 +08:00
|
|
|
// only quote keys if necessary
|
2019-09-27 23:42:02 +08:00
|
|
|
const text = isSimpleIdentifier(node.content)
|
|
|
|
? node.content
|
|
|
|
: JSON.stringify(node.content)
|
2019-09-24 09:22:52 +08:00
|
|
|
push(text, node)
|
2019-09-23 04:50:57 +08:00
|
|
|
} else {
|
2019-09-27 23:42:02 +08:00
|
|
|
push(`[${node.content}]`, node)
|
2019-09-23 14:52:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-20 11:05:51 +08:00
|
|
|
function genComment(node: CommentNode, context: CodegenContext) {
|
2021-04-28 23:22:16 +08:00
|
|
|
const { push, helper, pure } = context
|
|
|
|
if (pure) {
|
|
|
|
push(PURE_ANNOTATION)
|
2019-09-25 03:49:02 +08:00
|
|
|
}
|
2021-04-28 23:22:16 +08:00
|
|
|
push(`${helper(CREATE_COMMENT)}(${JSON.stringify(node.content)})`, node)
|
2019-09-20 11:05:51 +08:00
|
|
|
}
|
|
|
|
|
2020-02-12 07:12:56 +08:00
|
|
|
function genVNodeCall(node: VNodeCall, context: CodegenContext) {
|
2020-05-02 06:36:34 +08:00
|
|
|
const { push, helper, pure } = context
|
2020-02-12 07:12:56 +08:00
|
|
|
const {
|
|
|
|
tag,
|
|
|
|
props,
|
|
|
|
children,
|
|
|
|
patchFlag,
|
|
|
|
dynamicProps,
|
|
|
|
directives,
|
|
|
|
isBlock,
|
2021-06-23 07:15:20 +08:00
|
|
|
disableTracking,
|
|
|
|
isComponent
|
2020-02-12 07:12:56 +08:00
|
|
|
} = node
|
|
|
|
if (directives) {
|
|
|
|
push(helper(WITH_DIRECTIVES) + `(`)
|
|
|
|
}
|
|
|
|
if (isBlock) {
|
2020-06-18 04:13:14 +08:00
|
|
|
push(`(${helper(OPEN_BLOCK)}(${disableTracking ? `true` : ``}), `)
|
2020-02-12 07:12:56 +08:00
|
|
|
}
|
2020-05-02 06:36:34 +08:00
|
|
|
if (pure) {
|
|
|
|
push(PURE_ANNOTATION)
|
|
|
|
}
|
2021-06-23 07:15:20 +08:00
|
|
|
const callHelper: symbol = isBlock
|
|
|
|
? getVNodeBlockHelper(context.inSSR, isComponent)
|
|
|
|
: getVNodeHelper(context.inSSR, isComponent)
|
|
|
|
push(helper(callHelper) + `(`, node)
|
2020-02-12 07:12:56 +08:00
|
|
|
genNodeList(
|
|
|
|
genNullableArgs([tag, props, children, patchFlag, dynamicProps]),
|
|
|
|
context
|
|
|
|
)
|
|
|
|
push(`)`)
|
|
|
|
if (isBlock) {
|
|
|
|
push(`)`)
|
|
|
|
}
|
|
|
|
if (directives) {
|
|
|
|
push(`, `)
|
|
|
|
genNode(directives, context)
|
|
|
|
push(`)`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function genNullableArgs(args: any[]): CallExpression['arguments'] {
|
|
|
|
let i = args.length
|
|
|
|
while (i--) {
|
|
|
|
if (args[i] != null) break
|
|
|
|
}
|
|
|
|
return args.slice(0, i + 1).map(arg => arg || `null`)
|
|
|
|
}
|
|
|
|
|
2019-09-23 04:50:57 +08:00
|
|
|
// JavaScript
|
2019-10-02 04:48:20 +08:00
|
|
|
function genCallExpression(node: CallExpression, context: CodegenContext) {
|
2020-05-02 06:36:34 +08:00
|
|
|
const { push, helper, pure } = context
|
|
|
|
const callee = isString(node.callee) ? node.callee : helper(node.callee)
|
|
|
|
if (pure) {
|
|
|
|
push(PURE_ANNOTATION)
|
|
|
|
}
|
|
|
|
push(callee + `(`, node)
|
2019-10-02 04:48:20 +08:00
|
|
|
genNodeList(node.arguments, context)
|
2020-05-02 06:36:34 +08:00
|
|
|
push(`)`)
|
2019-09-23 04:50:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function genObjectExpression(node: ObjectExpression, context: CodegenContext) {
|
2019-12-14 01:56:25 +08:00
|
|
|
const { push, indent, deindent, newline } = context
|
2019-09-23 04:50:57 +08:00
|
|
|
const { properties } = node
|
2019-10-03 11:10:41 +08:00
|
|
|
if (!properties.length) {
|
|
|
|
push(`{}`, node)
|
|
|
|
return
|
|
|
|
}
|
2019-09-28 10:49:20 +08:00
|
|
|
const multilines =
|
|
|
|
properties.length > 1 ||
|
|
|
|
((!__BROWSER__ || __DEV__) &&
|
|
|
|
properties.some(p => p.value.type !== NodeTypes.SIMPLE_EXPRESSION))
|
2019-09-27 02:55:53 +08:00
|
|
|
push(multilines ? `{` : `{ `)
|
2019-09-23 04:50:57 +08:00
|
|
|
multilines && indent()
|
|
|
|
for (let i = 0; i < properties.length; i++) {
|
2019-12-14 01:56:25 +08:00
|
|
|
const { key, value } = properties[i]
|
2019-09-23 04:50:57 +08:00
|
|
|
// key
|
|
|
|
genExpressionAsPropertyKey(key, context)
|
|
|
|
push(`: `)
|
|
|
|
// value
|
2019-09-26 00:39:46 +08:00
|
|
|
genNode(value, context)
|
2019-09-23 04:50:57 +08:00
|
|
|
if (i < properties.length - 1) {
|
2019-09-25 04:35:01 +08:00
|
|
|
// will only reach this if it's multilines
|
|
|
|
push(`,`)
|
|
|
|
newline()
|
2019-09-23 04:50:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
multilines && deindent()
|
2019-11-16 05:47:55 +08:00
|
|
|
push(multilines ? `}` : ` }`)
|
2019-09-23 04:50:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function genArrayExpression(node: ArrayExpression, context: CodegenContext) {
|
2021-07-09 04:12:04 +08:00
|
|
|
genNodeListAsArray(node.elements as CodegenNode[], context)
|
2019-09-23 04:50:57 +08:00
|
|
|
}
|
2019-09-28 10:25:32 +08:00
|
|
|
|
2019-10-02 04:48:20 +08:00
|
|
|
function genFunctionExpression(
|
|
|
|
node: FunctionExpression,
|
2019-09-28 10:25:32 +08:00
|
|
|
context: CodegenContext
|
|
|
|
) {
|
2021-07-15 06:12:38 +08:00
|
|
|
const { push, indent, deindent } = context
|
2020-02-02 13:05:27 +08:00
|
|
|
const { params, returns, body, newline, isSlot } = node
|
2021-03-06 00:10:06 +08:00
|
|
|
if (isSlot) {
|
|
|
|
// wrap slot functions with owner context
|
2021-07-15 06:12:38 +08:00
|
|
|
push(`_${helperNameMap[WITH_CTX]}(`)
|
2019-12-17 01:11:51 +08:00
|
|
|
}
|
2019-10-02 04:48:20 +08:00
|
|
|
push(`(`, node)
|
|
|
|
if (isArray(params)) {
|
|
|
|
genNodeList(params, context)
|
|
|
|
} else if (params) {
|
|
|
|
genNode(params, context)
|
|
|
|
}
|
|
|
|
push(`) => `)
|
2020-02-02 13:05:27 +08:00
|
|
|
if (newline || body) {
|
2019-10-02 04:48:20 +08:00
|
|
|
push(`{`)
|
|
|
|
indent()
|
|
|
|
}
|
2020-02-02 13:05:27 +08:00
|
|
|
if (returns) {
|
|
|
|
if (newline) {
|
|
|
|
push(`return `)
|
|
|
|
}
|
|
|
|
if (isArray(returns)) {
|
|
|
|
genNodeListAsArray(returns, context)
|
|
|
|
} else {
|
|
|
|
genNode(returns, context)
|
|
|
|
}
|
|
|
|
} else if (body) {
|
|
|
|
genNode(body, context)
|
2019-10-02 04:48:20 +08:00
|
|
|
}
|
2020-02-02 13:05:27 +08:00
|
|
|
if (newline || body) {
|
2019-10-02 04:48:20 +08:00
|
|
|
deindent()
|
|
|
|
push(`}`)
|
|
|
|
}
|
2021-03-06 00:10:06 +08:00
|
|
|
if (isSlot) {
|
2021-05-05 23:06:04 +08:00
|
|
|
if (__COMPAT__ && node.isNonScopedSlot) {
|
|
|
|
push(`, undefined, true`)
|
|
|
|
}
|
2020-03-17 01:06:37 +08:00
|
|
|
push(`)`)
|
2019-12-17 01:11:51 +08:00
|
|
|
}
|
2019-09-28 10:25:32 +08:00
|
|
|
}
|
2019-10-01 21:27:34 +08:00
|
|
|
|
|
|
|
function genConditionalExpression(
|
|
|
|
node: ConditionalExpression,
|
|
|
|
context: CodegenContext
|
|
|
|
) {
|
2020-02-05 05:47:12 +08:00
|
|
|
const { test, consequent, alternate, newline: needNewline } = node
|
2019-10-01 21:27:34 +08:00
|
|
|
const { push, indent, deindent, newline } = context
|
|
|
|
if (test.type === NodeTypes.SIMPLE_EXPRESSION) {
|
2019-10-05 02:34:26 +08:00
|
|
|
const needsParens = !isSimpleIdentifier(test.content)
|
2019-10-06 03:42:49 +08:00
|
|
|
needsParens && push(`(`)
|
2019-10-01 21:27:34 +08:00
|
|
|
genExpression(test, context)
|
2019-10-05 02:34:26 +08:00
|
|
|
needsParens && push(`)`)
|
2019-10-01 21:27:34 +08:00
|
|
|
} else {
|
2019-10-05 02:34:26 +08:00
|
|
|
push(`(`)
|
2020-02-05 05:47:12 +08:00
|
|
|
genNode(test, context)
|
2019-10-05 02:34:26 +08:00
|
|
|
push(`)`)
|
2019-10-01 21:27:34 +08:00
|
|
|
}
|
2020-02-05 05:47:12 +08:00
|
|
|
needNewline && indent()
|
2019-10-01 21:27:34 +08:00
|
|
|
context.indentLevel++
|
2020-02-05 10:03:16 +08:00
|
|
|
needNewline || push(` `)
|
2019-10-01 21:27:34 +08:00
|
|
|
push(`? `)
|
|
|
|
genNode(consequent, context)
|
|
|
|
context.indentLevel--
|
2020-02-05 05:47:12 +08:00
|
|
|
needNewline && newline()
|
2020-02-05 10:03:16 +08:00
|
|
|
needNewline || push(` `)
|
2019-10-01 21:27:34 +08:00
|
|
|
push(`: `)
|
2019-10-02 00:25:13 +08:00
|
|
|
const isNested = alternate.type === NodeTypes.JS_CONDITIONAL_EXPRESSION
|
|
|
|
if (!isNested) {
|
|
|
|
context.indentLevel++
|
|
|
|
}
|
2019-10-01 21:27:34 +08:00
|
|
|
genNode(alternate, context)
|
2019-10-02 00:25:13 +08:00
|
|
|
if (!isNested) {
|
|
|
|
context.indentLevel--
|
|
|
|
}
|
2020-02-05 05:47:12 +08:00
|
|
|
needNewline && deindent(true /* without newline */)
|
2019-10-01 21:27:34 +08:00
|
|
|
}
|
|
|
|
|
2019-10-19 09:51:34 +08:00
|
|
|
function genCacheExpression(node: CacheExpression, context: CodegenContext) {
|
2019-10-24 05:57:40 +08:00
|
|
|
const { push, helper, indent, deindent, newline } = context
|
|
|
|
push(`_cache[${node.index}] || (`)
|
|
|
|
if (node.isVNode) {
|
|
|
|
indent()
|
|
|
|
push(`${helper(SET_BLOCK_TRACKING)}(-1),`)
|
|
|
|
newline()
|
|
|
|
}
|
|
|
|
push(`_cache[${node.index}] = `)
|
2019-10-19 09:51:34 +08:00
|
|
|
genNode(node.value, context)
|
2019-10-24 05:57:40 +08:00
|
|
|
if (node.isVNode) {
|
|
|
|
push(`,`)
|
|
|
|
newline()
|
|
|
|
push(`${helper(SET_BLOCK_TRACKING)}(1),`)
|
|
|
|
newline()
|
|
|
|
push(`_cache[${node.index}]`)
|
|
|
|
deindent()
|
|
|
|
}
|
|
|
|
push(`)`)
|
2019-10-19 09:51:34 +08:00
|
|
|
}
|
2020-02-02 13:05:27 +08:00
|
|
|
|
|
|
|
function genTemplateLiteral(node: TemplateLiteral, context: CodegenContext) {
|
2020-02-05 04:58:54 +08:00
|
|
|
const { push, indent, deindent } = context
|
2020-02-02 13:05:27 +08:00
|
|
|
push('`')
|
2020-02-05 05:47:12 +08:00
|
|
|
const l = node.elements.length
|
|
|
|
const multilines = l > 3
|
|
|
|
for (let i = 0; i < l; i++) {
|
2020-02-02 13:05:27 +08:00
|
|
|
const e = node.elements[i]
|
|
|
|
if (isString(e)) {
|
2020-05-27 02:27:01 +08:00
|
|
|
push(e.replace(/(`|\$|\\)/g, '\\$1'))
|
2020-02-02 13:05:27 +08:00
|
|
|
} else {
|
|
|
|
push('${')
|
2020-02-05 05:47:12 +08:00
|
|
|
if (multilines) indent()
|
2020-02-02 13:05:27 +08:00
|
|
|
genNode(e, context)
|
2020-02-05 05:47:12 +08:00
|
|
|
if (multilines) deindent()
|
2020-02-02 13:05:27 +08:00
|
|
|
push('}')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
push('`')
|
|
|
|
}
|
2020-02-03 10:35:28 +08:00
|
|
|
|
|
|
|
function genIfStatement(node: IfStatement, context: CodegenContext) {
|
|
|
|
const { push, indent, deindent } = context
|
|
|
|
const { test, consequent, alternate } = node
|
|
|
|
push(`if (`)
|
|
|
|
genNode(test, context)
|
|
|
|
push(`) {`)
|
|
|
|
indent()
|
|
|
|
genNode(consequent, context)
|
|
|
|
deindent()
|
|
|
|
push(`}`)
|
|
|
|
if (alternate) {
|
|
|
|
push(` else `)
|
|
|
|
if (alternate.type === NodeTypes.JS_IF_STATEMENT) {
|
|
|
|
genIfStatement(alternate, context)
|
|
|
|
} else {
|
|
|
|
push(`{`)
|
|
|
|
indent()
|
|
|
|
genNode(alternate, context)
|
|
|
|
deindent()
|
|
|
|
push(`}`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-05 11:49:47 +08:00
|
|
|
|
|
|
|
function genAssignmentExpression(
|
|
|
|
node: AssignmentExpression,
|
|
|
|
context: CodegenContext
|
|
|
|
) {
|
|
|
|
genNode(node.left, context)
|
|
|
|
context.push(` = `)
|
|
|
|
genNode(node.right, context)
|
|
|
|
}
|
2020-02-07 14:06:51 +08:00
|
|
|
|
2020-03-17 06:14:49 +08:00
|
|
|
function genSequenceExpression(
|
|
|
|
node: SequenceExpression,
|
|
|
|
context: CodegenContext
|
|
|
|
) {
|
|
|
|
context.push(`(`)
|
|
|
|
genNodeList(node.expressions, context)
|
|
|
|
context.push(`)`)
|
|
|
|
}
|
|
|
|
|
2020-02-07 14:06:51 +08:00
|
|
|
function genReturnStatement(
|
|
|
|
{ returns }: ReturnStatement,
|
|
|
|
context: CodegenContext
|
|
|
|
) {
|
|
|
|
context.push(`return `)
|
|
|
|
if (isArray(returns)) {
|
|
|
|
genNodeListAsArray(returns, context)
|
|
|
|
} else {
|
|
|
|
genNode(returns, context)
|
|
|
|
}
|
|
|
|
}
|