wip(ssr): initial scaffold for compiler-ssr
This commit is contained in:
@@ -45,7 +45,12 @@ export const enum NodeTypes {
|
||||
JS_FUNCTION_EXPRESSION,
|
||||
JS_SEQUENCE_EXPRESSION,
|
||||
JS_CONDITIONAL_EXPRESSION,
|
||||
JS_CACHE_EXPRESSION
|
||||
JS_CACHE_EXPRESSION,
|
||||
|
||||
// ssr codegen
|
||||
JS_BLOCK_STATEMENT,
|
||||
JS_TEMPLATE_LITERAL,
|
||||
JS_IF_STATEMENT
|
||||
}
|
||||
|
||||
export const enum ElementTypes {
|
||||
@@ -97,7 +102,7 @@ export interface RootNode extends Node {
|
||||
hoists: JSChildNode[]
|
||||
imports: ImportItem[]
|
||||
cached: number
|
||||
codegenNode: TemplateChildNode | JSChildNode | undefined
|
||||
codegenNode: TemplateChildNode | JSChildNode | BlockStatement | undefined
|
||||
}
|
||||
|
||||
export type ElementNode =
|
||||
@@ -130,6 +135,7 @@ export interface PlainElementNode extends BaseElementNode {
|
||||
| CacheExpression // when cached by v-once
|
||||
| SequenceExpression // when turned into a block
|
||||
| undefined
|
||||
ssrCodegenNode?: TemplateLiteral
|
||||
}
|
||||
|
||||
export interface ComponentNode extends BaseElementNode {
|
||||
@@ -147,7 +153,7 @@ export interface SlotOutletNode extends BaseElementNode {
|
||||
|
||||
export interface TemplateNode extends BaseElementNode {
|
||||
tagType: ElementTypes.TEMPLATE
|
||||
codegenNode: ElementCodegenNode | undefined | CacheExpression
|
||||
// TemplateNode is a container type that always gets compiled away
|
||||
}
|
||||
|
||||
export interface TextNode extends Node {
|
||||
@@ -232,9 +238,12 @@ export interface TextCallNode extends Node {
|
||||
codegenNode: CallExpression
|
||||
}
|
||||
|
||||
// JS Node Types ---------------------------------------------------------------
|
||||
|
||||
// We also include a number of JavaScript AST nodes for code generation.
|
||||
// The AST is an intentionally minimal subset just to meet the exact needs of
|
||||
// Vue render function generation.
|
||||
|
||||
export type JSChildNode =
|
||||
| CallExpression
|
||||
| ObjectExpression
|
||||
@@ -252,6 +261,7 @@ export interface CallExpression extends Node {
|
||||
| string
|
||||
| symbol
|
||||
| JSChildNode
|
||||
| SSRCodegenNode
|
||||
| TemplateChildNode
|
||||
| TemplateChildNode[])[]
|
||||
}
|
||||
@@ -275,8 +285,10 @@ export interface ArrayExpression extends Node {
|
||||
export interface FunctionExpression extends Node {
|
||||
type: NodeTypes.JS_FUNCTION_EXPRESSION
|
||||
params: ExpressionNode | ExpressionNode[] | undefined
|
||||
returns: TemplateChildNode | TemplateChildNode[] | JSChildNode
|
||||
returns?: TemplateChildNode | TemplateChildNode[] | JSChildNode
|
||||
body?: BlockStatement
|
||||
newline: boolean
|
||||
// so that codegen knows it needs to generate ScopeId wrapper
|
||||
isSlot: boolean
|
||||
}
|
||||
|
||||
@@ -299,6 +311,27 @@ export interface CacheExpression extends Node {
|
||||
isVNode: boolean
|
||||
}
|
||||
|
||||
// SSR-specific Node Types -----------------------------------------------------
|
||||
|
||||
export type SSRCodegenNode = BlockStatement | TemplateLiteral | IfStatement
|
||||
|
||||
export interface BlockStatement extends Node {
|
||||
type: NodeTypes.JS_BLOCK_STATEMENT
|
||||
body: (JSChildNode | IfStatement)[]
|
||||
}
|
||||
|
||||
export interface TemplateLiteral extends Node {
|
||||
type: NodeTypes.JS_TEMPLATE_LITERAL
|
||||
elements: (string | JSChildNode)[]
|
||||
}
|
||||
|
||||
export interface IfStatement extends Node {
|
||||
type: NodeTypes.JS_IF_STATEMENT
|
||||
test: ExpressionNode
|
||||
consequent: BlockStatement
|
||||
alternate: IfStatement | BlockStatement
|
||||
}
|
||||
|
||||
// Codegen Node Types ----------------------------------------------------------
|
||||
|
||||
// createVNode(...)
|
||||
@@ -637,3 +670,13 @@ export function createCacheExpression(
|
||||
loc: locStub
|
||||
}
|
||||
}
|
||||
|
||||
export function createTemplateLiteral(
|
||||
elements: TemplateLiteral['elements']
|
||||
): TemplateLiteral {
|
||||
return {
|
||||
type: NodeTypes.JS_TEMPLATE_LITERAL,
|
||||
elements,
|
||||
loc: locStub
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@ import {
|
||||
SequenceExpression,
|
||||
ConditionalExpression,
|
||||
CacheExpression,
|
||||
locStub
|
||||
locStub,
|
||||
SSRCodegenNode,
|
||||
TemplateLiteral
|
||||
} from './ast'
|
||||
import { SourceMapGenerator, RawSourceMap } from 'source-map'
|
||||
import {
|
||||
@@ -44,7 +46,7 @@ import {
|
||||
} from './runtimeHelpers'
|
||||
import { ImportItem } from './transform'
|
||||
|
||||
type CodegenNode = TemplateChildNode | JSChildNode
|
||||
type CodegenNode = TemplateChildNode | JSChildNode | SSRCodegenNode
|
||||
|
||||
export interface CodegenResult {
|
||||
code: string
|
||||
@@ -74,7 +76,8 @@ function createCodegenContext(
|
||||
prefixIdentifiers = mode === 'module' || mode === 'cjs',
|
||||
sourceMap = false,
|
||||
filename = `template.vue.html`,
|
||||
scopeId = null
|
||||
scopeId = null,
|
||||
ssr = false
|
||||
}: CodegenOptions
|
||||
): CodegenContext {
|
||||
const context: CodegenContext = {
|
||||
@@ -83,6 +86,7 @@ function createCodegenContext(
|
||||
sourceMap,
|
||||
filename,
|
||||
scopeId,
|
||||
ssr,
|
||||
source: ast.loc.source,
|
||||
code: ``,
|
||||
column: 1,
|
||||
@@ -169,7 +173,8 @@ export function generate(
|
||||
indent,
|
||||
deindent,
|
||||
newline,
|
||||
scopeId
|
||||
scopeId,
|
||||
ssr
|
||||
} = context
|
||||
const hasHelpers = ast.helpers.length > 0
|
||||
const useWithBlock = !prefixIdentifiers && mode !== 'module'
|
||||
@@ -231,10 +236,14 @@ export function generate(
|
||||
}
|
||||
|
||||
// enter render function
|
||||
if (genScopeId) {
|
||||
if (genScopeId && !ssr) {
|
||||
push(`const render = withId(`)
|
||||
}
|
||||
push(`function render() {`)
|
||||
if (!ssr) {
|
||||
push(`function render() {`)
|
||||
} else {
|
||||
push(`function ssrRender(_ctx, _push, _parent) {`)
|
||||
}
|
||||
indent()
|
||||
|
||||
if (useWithBlock) {
|
||||
@@ -255,7 +264,7 @@ export function generate(
|
||||
}
|
||||
newline()
|
||||
}
|
||||
} else {
|
||||
} else if (!ssr) {
|
||||
push(`const _ctx = this`)
|
||||
if (ast.cached > 0) {
|
||||
newline()
|
||||
@@ -276,7 +285,9 @@ export function generate(
|
||||
}
|
||||
|
||||
// generate the VNode tree expression
|
||||
push(`return `)
|
||||
if (!ssr) {
|
||||
push(`return `)
|
||||
}
|
||||
if (ast.codegenNode) {
|
||||
genNode(ast.codegenNode, context)
|
||||
} else {
|
||||
@@ -291,7 +302,7 @@ export function generate(
|
||||
deindent()
|
||||
push(`}`)
|
||||
|
||||
if (genScopeId) {
|
||||
if (genScopeId && !ssr) {
|
||||
push(`)`)
|
||||
}
|
||||
|
||||
@@ -325,7 +336,7 @@ function genHoists(hoists: JSChildNode[], context: CodegenContext) {
|
||||
return
|
||||
}
|
||||
const { push, newline, helper, scopeId, mode } = context
|
||||
const genScopeId = !__BROWSER__ && scopeId != null && mode === 'module'
|
||||
const genScopeId = !__BROWSER__ && scopeId != null && mode !== 'function'
|
||||
newline()
|
||||
|
||||
// push scope Id before initilaizing hoisted vnodes so that these vnodes
|
||||
@@ -469,6 +480,18 @@ function genNode(node: CodegenNode | symbol | string, context: CodegenContext) {
|
||||
case NodeTypes.JS_CACHE_EXPRESSION:
|
||||
genCacheExpression(node, context)
|
||||
break
|
||||
|
||||
// SSR only types
|
||||
case NodeTypes.JS_BLOCK_STATEMENT:
|
||||
!__BROWSER__ && genNodeList(node.body, context, true)
|
||||
break
|
||||
case NodeTypes.JS_TEMPLATE_LITERAL:
|
||||
!__BROWSER__ && genTemplateLiteral(node, context)
|
||||
break
|
||||
case NodeTypes.JS_IF_STATEMENT:
|
||||
// TODO
|
||||
break
|
||||
|
||||
/* istanbul ignore next */
|
||||
default:
|
||||
if (__DEV__) {
|
||||
@@ -589,10 +612,10 @@ function genFunctionExpression(
|
||||
context: CodegenContext
|
||||
) {
|
||||
const { push, indent, deindent, scopeId, mode } = context
|
||||
const { params, returns, newline, isSlot } = node
|
||||
const { params, returns, body, newline, isSlot } = node
|
||||
// slot functions also need to push scopeId before rendering its content
|
||||
const genScopeId =
|
||||
!__BROWSER__ && isSlot && scopeId != null && mode === 'module'
|
||||
!__BROWSER__ && isSlot && scopeId != null && mode !== 'function'
|
||||
|
||||
if (genScopeId) {
|
||||
push(`withId(`)
|
||||
@@ -604,17 +627,23 @@ function genFunctionExpression(
|
||||
genNode(params, context)
|
||||
}
|
||||
push(`) => `)
|
||||
if (newline) {
|
||||
if (newline || body) {
|
||||
push(`{`)
|
||||
indent()
|
||||
push(`return `)
|
||||
}
|
||||
if (isArray(returns)) {
|
||||
genNodeListAsArray(returns, context)
|
||||
} else {
|
||||
genNode(returns, context)
|
||||
if (returns) {
|
||||
if (newline) {
|
||||
push(`return `)
|
||||
}
|
||||
if (isArray(returns)) {
|
||||
genNodeListAsArray(returns, context)
|
||||
} else {
|
||||
genNode(returns, context)
|
||||
}
|
||||
} else if (body) {
|
||||
genNode(body, context)
|
||||
}
|
||||
if (newline) {
|
||||
if (newline || body) {
|
||||
deindent()
|
||||
push(`}`)
|
||||
}
|
||||
@@ -686,3 +715,19 @@ function genCacheExpression(node: CacheExpression, context: CodegenContext) {
|
||||
}
|
||||
push(`)`)
|
||||
}
|
||||
|
||||
function genTemplateLiteral(node: TemplateLiteral, context: CodegenContext) {
|
||||
const { push } = context
|
||||
push('`')
|
||||
for (let i = 0; i < node.elements.length; i++) {
|
||||
const e = node.elements[i]
|
||||
if (isString(e)) {
|
||||
push(e.replace(/`/g, '\\`'))
|
||||
} else {
|
||||
push('${')
|
||||
genNode(e, context)
|
||||
push('}')
|
||||
}
|
||||
}
|
||||
push('`')
|
||||
}
|
||||
|
||||
@@ -31,6 +31,11 @@ export { registerRuntimeHelpers } from './runtimeHelpers'
|
||||
export { transformModel } from './transforms/vModel'
|
||||
export { transformOn } from './transforms/vOn'
|
||||
|
||||
// exported for compiler-ssr
|
||||
export { transformExpression } from './transforms/transformExpression'
|
||||
export { trackVForSlotScopes, trackSlotScopes } from './transforms/vSlot'
|
||||
export { buildProps } from './transforms/transformElement'
|
||||
|
||||
// utility, but need to rewrite typing to avoid dts relying on @vue/shared
|
||||
import { generateCodeFrame as _genCodeFrame } from '@vue/shared'
|
||||
const generateCodeFrame = _genCodeFrame as (
|
||||
|
||||
@@ -29,7 +29,7 @@ export interface ParserOptions {
|
||||
|
||||
export interface TransformOptions {
|
||||
nodeTransforms?: NodeTransform[]
|
||||
directiveTransforms?: { [name: string]: DirectiveTransform }
|
||||
directiveTransforms?: { [name: string]: DirectiveTransform | undefined }
|
||||
isBuiltInComponent?: (tag: string) => symbol | void
|
||||
// Transform expressions like {{ foo }} to `_ctx.foo`.
|
||||
// - This is force-enabled in module mode, since modules are by default strict
|
||||
@@ -76,6 +76,8 @@ export interface CodegenOptions {
|
||||
filename?: string
|
||||
// SFC scoped styles ID
|
||||
scopeId?: string | null
|
||||
// generate SSR specific code?
|
||||
ssr?: boolean
|
||||
}
|
||||
|
||||
export type CompilerOptions = ParserOptions & TransformOptions & CodegenOptions
|
||||
|
||||
Reference in New Issue
Block a user