wip(compiler-ssr): v-if
This commit is contained in:
parent
090eb0ce67
commit
e8c5de6cfd
@ -5,7 +5,7 @@ exports[`compiler: integration tests function mode 1`] = `
|
|||||||
|
|
||||||
return function render() {
|
return function render() {
|
||||||
with (this) {
|
with (this) {
|
||||||
const { toDisplayString: _toDisplayString, openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, createCommentVNode: _createCommentVNode, Fragment: _Fragment, renderList: _renderList, createTextVNode: _createTextVNode } = _Vue
|
const { toDisplayString: _toDisplayString, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode, Fragment: _Fragment, renderList: _renderList, createTextVNode: _createTextVNode } = _Vue
|
||||||
|
|
||||||
return (_openBlock(), _createBlock(\\"div\\", {
|
return (_openBlock(), _createBlock(\\"div\\", {
|
||||||
id: \\"foo\\",
|
id: \\"foo\\",
|
||||||
@ -26,7 +26,7 @@ return function render() {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: integration tests function mode w/ prefixIdentifiers: true 1`] = `
|
exports[`compiler: integration tests function mode w/ prefixIdentifiers: true 1`] = `
|
||||||
"const { toDisplayString, openBlock, createVNode, createBlock, createCommentVNode, Fragment, renderList, createTextVNode } = Vue
|
"const { toDisplayString, createVNode, openBlock, createBlock, createCommentVNode, Fragment, renderList, createTextVNode } = Vue
|
||||||
|
|
||||||
return function render() {
|
return function render() {
|
||||||
const _ctx = this
|
const _ctx = this
|
||||||
@ -48,7 +48,7 @@ return function render() {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: integration tests module mode 1`] = `
|
exports[`compiler: integration tests module mode 1`] = `
|
||||||
"import { toDisplayString, openBlock, createVNode, createBlock, createCommentVNode, Fragment, renderList, createTextVNode } from \\"vue\\"
|
"import { toDisplayString, createVNode, openBlock, createBlock, createCommentVNode, Fragment, renderList, createTextVNode } from \\"vue\\"
|
||||||
|
|
||||||
export function render() {
|
export function render() {
|
||||||
const _ctx = this
|
const _ctx = this
|
||||||
|
@ -380,7 +380,7 @@ const _hoisted_2 = _createVNode(\\"span\\")
|
|||||||
|
|
||||||
return function render() {
|
return function render() {
|
||||||
with (this) {
|
with (this) {
|
||||||
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
|
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
|
||||||
|
|
||||||
return (_openBlock(), _createBlock(\\"div\\", null, [
|
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||||
(_openBlock(), ok
|
(_openBlock(), ok
|
||||||
|
@ -155,7 +155,7 @@ exports[`compiler: v-for codegen v-if + v-for 1`] = `
|
|||||||
|
|
||||||
return function render() {
|
return function render() {
|
||||||
with (this) {
|
with (this) {
|
||||||
const { openBlock: _openBlock, renderList: _renderList, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode, createCommentVNode: _createCommentVNode } = _Vue
|
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode, createCommentVNode: _createCommentVNode } = _Vue
|
||||||
|
|
||||||
return (_openBlock(), ok
|
return (_openBlock(), ok
|
||||||
? _createBlock(_Fragment, { key: 0 }, _renderList(list, (i) => {
|
? _createBlock(_Fragment, { key: 0 }, _renderList(list, (i) => {
|
||||||
|
@ -5,7 +5,7 @@ exports[`compiler: v-if codegen basic v-if 1`] = `
|
|||||||
|
|
||||||
return function render() {
|
return function render() {
|
||||||
with (this) {
|
with (this) {
|
||||||
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
|
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
|
||||||
|
|
||||||
return (_openBlock(), ok
|
return (_openBlock(), ok
|
||||||
? _createBlock(\\"div\\", { key: 0 })
|
? _createBlock(\\"div\\", { key: 0 })
|
||||||
@ -19,7 +19,7 @@ exports[`compiler: v-if codegen template v-if 1`] = `
|
|||||||
|
|
||||||
return function render() {
|
return function render() {
|
||||||
with (this) {
|
with (this) {
|
||||||
const { openBlock: _openBlock, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
|
const { createVNode: _createVNode, openBlock: _openBlock, Fragment: _Fragment, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
|
||||||
|
|
||||||
return (_openBlock(), ok
|
return (_openBlock(), ok
|
||||||
? _createBlock(_Fragment, { key: 0 }, [
|
? _createBlock(_Fragment, { key: 0 }, [
|
||||||
@ -37,7 +37,7 @@ exports[`compiler: v-if codegen template v-if w/ single <slot/> child 1`] = `
|
|||||||
|
|
||||||
return function render() {
|
return function render() {
|
||||||
with (this) {
|
with (this) {
|
||||||
const { openBlock: _openBlock, renderSlot: _renderSlot, createCommentVNode: _createCommentVNode } = _Vue
|
const { renderSlot: _renderSlot, openBlock: _openBlock, createCommentVNode: _createCommentVNode } = _Vue
|
||||||
|
|
||||||
return (_openBlock(), ok
|
return (_openBlock(), ok
|
||||||
? _renderSlot($slots, \\"default\\", { key: 0 })
|
? _renderSlot($slots, \\"default\\", { key: 0 })
|
||||||
@ -51,7 +51,7 @@ exports[`compiler: v-if codegen v-if + v-else 1`] = `
|
|||||||
|
|
||||||
return function render() {
|
return function render() {
|
||||||
with (this) {
|
with (this) {
|
||||||
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
|
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
|
||||||
|
|
||||||
return (_openBlock(), ok
|
return (_openBlock(), ok
|
||||||
? _createBlock(\\"div\\", { key: 0 })
|
? _createBlock(\\"div\\", { key: 0 })
|
||||||
@ -65,7 +65,7 @@ exports[`compiler: v-if codegen v-if + v-else-if + v-else 1`] = `
|
|||||||
|
|
||||||
return function render() {
|
return function render() {
|
||||||
with (this) {
|
with (this) {
|
||||||
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, createCommentVNode: _createCommentVNode, Fragment: _Fragment } = _Vue
|
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode, Fragment: _Fragment } = _Vue
|
||||||
|
|
||||||
return (_openBlock(), ok
|
return (_openBlock(), ok
|
||||||
? _createBlock(\\"div\\", { key: 0 })
|
? _createBlock(\\"div\\", { key: 0 })
|
||||||
@ -81,7 +81,7 @@ exports[`compiler: v-if codegen v-if + v-else-if 1`] = `
|
|||||||
|
|
||||||
return function render() {
|
return function render() {
|
||||||
with (this) {
|
with (this) {
|
||||||
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
|
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
|
||||||
|
|
||||||
return (_openBlock(), ok
|
return (_openBlock(), ok
|
||||||
? _createBlock(\\"div\\", { key: 0 })
|
? _createBlock(\\"div\\", { key: 0 })
|
||||||
@ -97,7 +97,7 @@ exports[`compiler: v-if codegen v-if on <slot/> 1`] = `
|
|||||||
|
|
||||||
return function render() {
|
return function render() {
|
||||||
with (this) {
|
with (this) {
|
||||||
const { openBlock: _openBlock, renderSlot: _renderSlot, createCommentVNode: _createCommentVNode } = _Vue
|
const { renderSlot: _renderSlot, openBlock: _openBlock, createCommentVNode: _createCommentVNode } = _Vue
|
||||||
|
|
||||||
return (_openBlock(), ok
|
return (_openBlock(), ok
|
||||||
? _renderSlot($slots, \\"default\\", { key: 0 })
|
? _renderSlot($slots, \\"default\\", { key: 0 })
|
||||||
@ -111,7 +111,7 @@ exports[`compiler: v-if codegen v-if with key 1`] = `
|
|||||||
|
|
||||||
return function render() {
|
return function render() {
|
||||||
with (this) {
|
with (this) {
|
||||||
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
|
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
|
||||||
|
|
||||||
return (_openBlock(), ok
|
return (_openBlock(), ok
|
||||||
? _createBlock(\\"div\\", { key: \\"some-key\\" })
|
? _createBlock(\\"div\\", { key: \\"some-key\\" })
|
||||||
|
@ -12,7 +12,8 @@ import {
|
|||||||
SimpleExpressionNode,
|
SimpleExpressionNode,
|
||||||
SequenceExpression,
|
SequenceExpression,
|
||||||
ConditionalExpression,
|
ConditionalExpression,
|
||||||
CallExpression
|
CallExpression,
|
||||||
|
IfCodegenNode
|
||||||
} from '../../src/ast'
|
} from '../../src/ast'
|
||||||
import { ErrorCodes } from '../../src/errors'
|
import { ErrorCodes } from '../../src/errors'
|
||||||
import { CompilerOptions, generate } from '../../src'
|
import { CompilerOptions, generate } from '../../src'
|
||||||
@ -43,7 +44,7 @@ function parseWithIfTransform(
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
root: ast,
|
root: ast,
|
||||||
node: ast.children[returnIndex] as IfNode
|
node: ast.children[returnIndex] as IfNode & { codegenNode: IfCodegenNode }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ export interface RootNode extends Node {
|
|||||||
hoists: JSChildNode[]
|
hoists: JSChildNode[]
|
||||||
imports: ImportItem[]
|
imports: ImportItem[]
|
||||||
cached: number
|
cached: number
|
||||||
codegenNode: TemplateChildNode | JSChildNode | BlockStatement | undefined
|
codegenNode?: TemplateChildNode | JSChildNode | BlockStatement | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ElementNode =
|
export type ElementNode =
|
||||||
@ -213,7 +213,7 @@ export interface CompoundExpressionNode extends Node {
|
|||||||
export interface IfNode extends Node {
|
export interface IfNode extends Node {
|
||||||
type: NodeTypes.IF
|
type: NodeTypes.IF
|
||||||
branches: IfBranchNode[]
|
branches: IfBranchNode[]
|
||||||
codegenNode: IfCodegenNode
|
codegenNode?: IfCodegenNode
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IfBranchNode extends Node {
|
export interface IfBranchNode extends Node {
|
||||||
@ -229,7 +229,7 @@ export interface ForNode extends Node {
|
|||||||
keyAlias: ExpressionNode | undefined
|
keyAlias: ExpressionNode | undefined
|
||||||
objectIndexAlias: ExpressionNode | undefined
|
objectIndexAlias: ExpressionNode | undefined
|
||||||
children: TemplateChildNode[]
|
children: TemplateChildNode[]
|
||||||
codegenNode: ForCodegenNode
|
codegenNode?: ForCodegenNode
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TextCallNode extends Node {
|
export interface TextCallNode extends Node {
|
||||||
|
@ -32,7 +32,11 @@ export { transformModel } from './transforms/vModel'
|
|||||||
export { transformOn } from './transforms/vOn'
|
export { transformOn } from './transforms/vOn'
|
||||||
|
|
||||||
// exported for compiler-ssr
|
// exported for compiler-ssr
|
||||||
export { transformExpression } from './transforms/transformExpression'
|
export { processIfBranches } from './transforms/vIf'
|
||||||
|
export {
|
||||||
|
transformExpression,
|
||||||
|
processExpression
|
||||||
|
} from './transforms/transformExpression'
|
||||||
export { trackVForSlotScopes, trackSlotScopes } from './transforms/vSlot'
|
export { trackVForSlotScopes, trackSlotScopes } from './transforms/vSlot'
|
||||||
export { buildProps } from './transforms/transformElement'
|
export { buildProps } from './transforms/transformElement'
|
||||||
|
|
||||||
|
@ -23,7 +23,8 @@ import {
|
|||||||
BlockCodegenNode,
|
BlockCodegenNode,
|
||||||
SlotOutletCodegenNode,
|
SlotOutletCodegenNode,
|
||||||
ElementCodegenNode,
|
ElementCodegenNode,
|
||||||
ComponentCodegenNode
|
ComponentCodegenNode,
|
||||||
|
IfNode
|
||||||
} from '../ast'
|
} from '../ast'
|
||||||
import { createCompilerError, ErrorCodes } from '../errors'
|
import { createCompilerError, ErrorCodes } from '../errors'
|
||||||
import { processExpression } from './transformExpression'
|
import { processExpression } from './transformExpression'
|
||||||
@ -40,73 +41,18 @@ import { injectProp } from '../utils'
|
|||||||
export const transformIf = createStructuralDirectiveTransform(
|
export const transformIf = createStructuralDirectiveTransform(
|
||||||
/^(if|else|else-if)$/,
|
/^(if|else|else-if)$/,
|
||||||
(node, dir, context) => {
|
(node, dir, context) => {
|
||||||
if (
|
return processIfBranches(node, dir, context, (ifNode, branch, isRoot) => {
|
||||||
dir.name !== 'else' &&
|
|
||||||
(!dir.exp || !(dir.exp as SimpleExpressionNode).content.trim())
|
|
||||||
) {
|
|
||||||
const loc = dir.exp ? dir.exp.loc : node.loc
|
|
||||||
context.onError(
|
|
||||||
createCompilerError(ErrorCodes.X_V_IF_NO_EXPRESSION, dir.loc)
|
|
||||||
)
|
|
||||||
dir.exp = createSimpleExpression(`true`, false, loc)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!__BROWSER__ && context.prefixIdentifiers && dir.exp) {
|
|
||||||
// dir.exp can only be simple expression because vIf transform is applied
|
|
||||||
// before expression transform.
|
|
||||||
dir.exp = processExpression(dir.exp as SimpleExpressionNode, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dir.name === 'if') {
|
|
||||||
const branch = createIfBranch(node, dir)
|
|
||||||
const codegenNode = createSequenceExpression([
|
|
||||||
createCallExpression(context.helper(OPEN_BLOCK))
|
|
||||||
]) as IfCodegenNode
|
|
||||||
|
|
||||||
context.replaceNode({
|
|
||||||
type: NodeTypes.IF,
|
|
||||||
loc: node.loc,
|
|
||||||
branches: [branch],
|
|
||||||
codegenNode
|
|
||||||
})
|
|
||||||
|
|
||||||
// Exit callback. Complete the codegenNode when all children have been
|
// Exit callback. Complete the codegenNode when all children have been
|
||||||
// transformed.
|
// transformed.
|
||||||
return () => {
|
return () => {
|
||||||
codegenNode.expressions.push(createCodegenNodeForBranch(
|
if (isRoot) {
|
||||||
branch,
|
ifNode.codegenNode = createSequenceExpression([
|
||||||
0,
|
createCallExpression(context.helper(OPEN_BLOCK)),
|
||||||
context
|
createCodegenNodeForBranch(branch, 0, context)
|
||||||
) as IfConditionalExpression)
|
]) as IfCodegenNode
|
||||||
}
|
} else {
|
||||||
} else {
|
|
||||||
// locate the adjacent v-if
|
|
||||||
const siblings = context.parent!.children
|
|
||||||
const comments = []
|
|
||||||
let i = siblings.indexOf(node)
|
|
||||||
while (i-- >= -1) {
|
|
||||||
const sibling = siblings[i]
|
|
||||||
if (__DEV__ && sibling && sibling.type === NodeTypes.COMMENT) {
|
|
||||||
context.removeNode(sibling)
|
|
||||||
comments.unshift(sibling)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (sibling && sibling.type === NodeTypes.IF) {
|
|
||||||
// move the node to the if node's branches
|
|
||||||
context.removeNode()
|
|
||||||
const branch = createIfBranch(node, dir)
|
|
||||||
if (__DEV__ && comments.length) {
|
|
||||||
branch.children = [...comments, ...branch.children]
|
|
||||||
}
|
|
||||||
sibling.branches.push(branch)
|
|
||||||
// since the branch was removed, it will not be traversed.
|
|
||||||
// make sure to traverse here.
|
|
||||||
traverseChildren(branch, context)
|
|
||||||
// make sure to reset currentNode after traversal to indicate this
|
|
||||||
// node has been removed.
|
|
||||||
context.currentNode = null
|
|
||||||
// attach this branch's codegen node to the v-if root.
|
// attach this branch's codegen node to the v-if root.
|
||||||
let parentCondition = sibling.codegenNode
|
let parentCondition = ifNode.codegenNode!
|
||||||
.expressions[1] as ConditionalExpression
|
.expressions[1] as ConditionalExpression
|
||||||
while (
|
while (
|
||||||
parentCondition.alternate.type ===
|
parentCondition.alternate.type ===
|
||||||
@ -116,20 +62,92 @@ export const transformIf = createStructuralDirectiveTransform(
|
|||||||
}
|
}
|
||||||
parentCondition.alternate = createCodegenNodeForBranch(
|
parentCondition.alternate = createCodegenNodeForBranch(
|
||||||
branch,
|
branch,
|
||||||
sibling.branches.length - 1,
|
ifNode.branches.length - 1,
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
context.onError(
|
|
||||||
createCompilerError(ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, node.loc)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const processIfBranches = (
|
||||||
|
node: ElementNode,
|
||||||
|
dir: DirectiveNode,
|
||||||
|
context: TransformContext,
|
||||||
|
processCodegen?: (
|
||||||
|
node: IfNode,
|
||||||
|
branch: IfBranchNode,
|
||||||
|
isRoot: boolean
|
||||||
|
) => (() => void) | void
|
||||||
|
) => {
|
||||||
|
if (
|
||||||
|
dir.name !== 'else' &&
|
||||||
|
(!dir.exp || !(dir.exp as SimpleExpressionNode).content.trim())
|
||||||
|
) {
|
||||||
|
const loc = dir.exp ? dir.exp.loc : node.loc
|
||||||
|
context.onError(
|
||||||
|
createCompilerError(ErrorCodes.X_V_IF_NO_EXPRESSION, dir.loc)
|
||||||
|
)
|
||||||
|
dir.exp = createSimpleExpression(`true`, false, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!__BROWSER__ && context.prefixIdentifiers && dir.exp) {
|
||||||
|
// dir.exp can only be simple expression because vIf transform is applied
|
||||||
|
// before expression transform.
|
||||||
|
dir.exp = processExpression(dir.exp as SimpleExpressionNode, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir.name === 'if') {
|
||||||
|
const branch = createIfBranch(node, dir)
|
||||||
|
const ifNode: IfNode = {
|
||||||
|
type: NodeTypes.IF,
|
||||||
|
loc: node.loc,
|
||||||
|
branches: [branch]
|
||||||
|
}
|
||||||
|
context.replaceNode(ifNode)
|
||||||
|
if (processCodegen) {
|
||||||
|
return processCodegen(ifNode, branch, true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// locate the adjacent v-if
|
||||||
|
const siblings = context.parent!.children
|
||||||
|
const comments = []
|
||||||
|
let i = siblings.indexOf(node)
|
||||||
|
while (i-- >= -1) {
|
||||||
|
const sibling = siblings[i]
|
||||||
|
if (__DEV__ && sibling && sibling.type === NodeTypes.COMMENT) {
|
||||||
|
context.removeNode(sibling)
|
||||||
|
comments.unshift(sibling)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (sibling && sibling.type === NodeTypes.IF) {
|
||||||
|
// move the node to the if node's branches
|
||||||
|
context.removeNode()
|
||||||
|
const branch = createIfBranch(node, dir)
|
||||||
|
if (__DEV__ && comments.length) {
|
||||||
|
branch.children = [...comments, ...branch.children]
|
||||||
|
}
|
||||||
|
sibling.branches.push(branch)
|
||||||
|
const onExit = processCodegen && processCodegen(sibling, branch, false)
|
||||||
|
// since the branch was removed, it will not be traversed.
|
||||||
|
// make sure to traverse here.
|
||||||
|
traverseChildren(branch, context)
|
||||||
|
// call on exit
|
||||||
|
if (onExit) onExit()
|
||||||
|
// make sure to reset currentNode after traversal to indicate this
|
||||||
|
// node has been removed.
|
||||||
|
context.currentNode = null
|
||||||
|
} else {
|
||||||
|
context.onError(
|
||||||
|
createCompilerError(ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, node.loc)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function createIfBranch(node: ElementNode, dir: DirectiveNode): IfBranchNode {
|
function createIfBranch(node: ElementNode, dir: DirectiveNode): IfBranchNode {
|
||||||
return {
|
return {
|
||||||
type: NodeTypes.IF_BRANCH,
|
type: NodeTypes.IF_BRANCH,
|
||||||
@ -171,25 +189,25 @@ function createChildrenCodegenNode(
|
|||||||
createSimpleExpression(index + '', false)
|
createSimpleExpression(index + '', false)
|
||||||
)
|
)
|
||||||
const { children } = branch
|
const { children } = branch
|
||||||
const child = children[0]
|
const firstChild = children[0]
|
||||||
const needFragmentWrapper =
|
const needFragmentWrapper =
|
||||||
children.length !== 1 || child.type !== NodeTypes.ELEMENT
|
children.length !== 1 || firstChild.type !== NodeTypes.ELEMENT
|
||||||
if (needFragmentWrapper) {
|
if (needFragmentWrapper) {
|
||||||
const blockArgs: CallExpression['arguments'] = [
|
const blockArgs: CallExpression['arguments'] = [
|
||||||
helper(FRAGMENT),
|
helper(FRAGMENT),
|
||||||
createObjectExpression([keyProperty]),
|
createObjectExpression([keyProperty]),
|
||||||
children
|
children
|
||||||
]
|
]
|
||||||
if (children.length === 1 && child.type === NodeTypes.FOR) {
|
if (children.length === 1 && firstChild.type === NodeTypes.FOR) {
|
||||||
// optimize away nested fragments when child is a ForNode
|
// optimize away nested fragments when child is a ForNode
|
||||||
const forBlockArgs = child.codegenNode.expressions[1].arguments
|
const forBlockArgs = firstChild.codegenNode!.expressions[1].arguments
|
||||||
// directly use the for block's children and patchFlag
|
// directly use the for block's children and patchFlag
|
||||||
blockArgs[2] = forBlockArgs[2]
|
blockArgs[2] = forBlockArgs[2]
|
||||||
blockArgs[3] = forBlockArgs[3]
|
blockArgs[3] = forBlockArgs[3]
|
||||||
}
|
}
|
||||||
return createCallExpression(helper(CREATE_BLOCK), blockArgs)
|
return createCallExpression(helper(CREATE_BLOCK), blockArgs)
|
||||||
} else {
|
} else {
|
||||||
const childCodegen = (child as ElementNode).codegenNode as
|
const childCodegen = (firstChild as ElementNode).codegenNode as
|
||||||
| ElementCodegenNode
|
| ElementCodegenNode
|
||||||
| ComponentCodegenNode
|
| ComponentCodegenNode
|
||||||
| SlotOutletCodegenNode
|
| SlotOutletCodegenNode
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { getCompiledString } from './utils'
|
import { getCompiledString } from './utils'
|
||||||
|
|
||||||
describe('element', () => {
|
describe('ssr: element', () => {
|
||||||
test('basic elements', () => {
|
test('basic elements', () => {
|
||||||
expect(getCompiledString(`<div></div>`)).toMatchInlineSnapshot(
|
expect(getCompiledString(`<div></div>`)).toMatchInlineSnapshot(
|
||||||
`"\`<div></div>\`"`
|
`"\`<div></div>\`"`
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { getCompiledString } from './utils'
|
import { getCompiledString } from './utils'
|
||||||
|
|
||||||
describe('text', () => {
|
describe('ssr: text', () => {
|
||||||
test('static text', () => {
|
test('static text', () => {
|
||||||
expect(getCompiledString(`foo`)).toMatchInlineSnapshot(`"\`foo\`"`)
|
expect(getCompiledString(`foo`)).toMatchInlineSnapshot(`"\`foo\`"`)
|
||||||
})
|
})
|
||||||
|
141
packages/compiler-ssr/__tests__/ssrVIf.spec.ts
Normal file
141
packages/compiler-ssr/__tests__/ssrVIf.spec.ts
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import { compile } from '../src'
|
||||||
|
|
||||||
|
describe('ssr: v-if', () => {
|
||||||
|
test('basic', () => {
|
||||||
|
expect(compile(`<div v-if="foo"></div>`).code).toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
return function ssrRender(_ctx, _push, _parent) {
|
||||||
|
if (_ctx.foo) {
|
||||||
|
_push(\`<div></div>\`)
|
||||||
|
} else {
|
||||||
|
_push(\`<!---->\`)
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('with nested content', () => {
|
||||||
|
expect(compile(`<div v-if="foo">hello<span>ok</span></div>`).code)
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
return function ssrRender(_ctx, _push, _parent) {
|
||||||
|
if (_ctx.foo) {
|
||||||
|
_push(\`<div>hello<span>ok</span></div>\`)
|
||||||
|
} else {
|
||||||
|
_push(\`<!---->\`)
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('v-if + v-else', () => {
|
||||||
|
expect(compile(`<div v-if="foo"/><span v-else/>`).code)
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
return function ssrRender(_ctx, _push, _parent) {
|
||||||
|
if (_ctx.foo) {
|
||||||
|
_push(\`<div></div>\`)
|
||||||
|
} else {
|
||||||
|
_push(\`<span></span>\`)
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('v-if + v-else-if', () => {
|
||||||
|
expect(compile(`<div v-if="foo"/><span v-else-if="bar"/>`).code)
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
return function ssrRender(_ctx, _push, _parent) {
|
||||||
|
if (_ctx.foo) {
|
||||||
|
_push(\`<div></div>\`)
|
||||||
|
} else if (_ctx.bar) {
|
||||||
|
_push(\`<span></span>\`)
|
||||||
|
} else {
|
||||||
|
_push(\`<!---->\`)
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('v-if + v-else-if + v-else', () => {
|
||||||
|
expect(compile(`<div v-if="foo"/><span v-else-if="bar"/><p v-else/>`).code)
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
return function ssrRender(_ctx, _push, _parent) {
|
||||||
|
if (_ctx.foo) {
|
||||||
|
_push(\`<div></div>\`)
|
||||||
|
} else if (_ctx.bar) {
|
||||||
|
_push(\`<span></span>\`)
|
||||||
|
} else {
|
||||||
|
_push(\`<p></p>\`)
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('<template v-if> (text)', () => {
|
||||||
|
expect(compile(`<template v-if="foo">hello</template>`).code)
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
return function ssrRender(_ctx, _push, _parent) {
|
||||||
|
if (_ctx.foo) {
|
||||||
|
_push(\`<!---->hello<!---->\`)
|
||||||
|
} else {
|
||||||
|
_push(\`<!---->\`)
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('<template v-if> (single element)', () => {
|
||||||
|
// single element should not wrap with fragment
|
||||||
|
expect(compile(`<template v-if="foo"><div>hi</div></template>`).code)
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
return function ssrRender(_ctx, _push, _parent) {
|
||||||
|
if (_ctx.foo) {
|
||||||
|
_push(\`<div>hi</div>\`)
|
||||||
|
} else {
|
||||||
|
_push(\`<!---->\`)
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('<template v-if> (multiple element)', () => {
|
||||||
|
expect(
|
||||||
|
compile(`<template v-if="foo"><div>hi</div><div>ho</div></template>`).code
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
return function ssrRender(_ctx, _push, _parent) {
|
||||||
|
if (_ctx.foo) {
|
||||||
|
_push(\`<!----><div>hi</div><div>ho</div><!---->\`)
|
||||||
|
} else {
|
||||||
|
_push(\`<!---->\`)
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('<template v-if> (with v-for inside)', () => {
|
||||||
|
// TODO should not contain nested fragments
|
||||||
|
})
|
||||||
|
|
||||||
|
test('<template v-if> + normal v-else', () => {
|
||||||
|
expect(
|
||||||
|
compile(
|
||||||
|
`<template v-if="foo"><div>hi</div><div>ho</div></template><div v-else/>`
|
||||||
|
).code
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
"
|
||||||
|
return function ssrRender(_ctx, _push, _parent) {
|
||||||
|
if (_ctx.foo) {
|
||||||
|
_push(\`<!----><div>hi</div><div>ho</div><!---->\`)
|
||||||
|
} else {
|
||||||
|
_push(\`<div></div>\`)
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
})
|
@ -1,7 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
RootNode,
|
RootNode,
|
||||||
BlockStatement,
|
BlockStatement,
|
||||||
CallExpression,
|
|
||||||
TemplateLiteral,
|
TemplateLiteral,
|
||||||
createCallExpression,
|
createCallExpression,
|
||||||
createTemplateLiteral,
|
createTemplateLiteral,
|
||||||
@ -10,10 +9,13 @@ import {
|
|||||||
ElementTypes,
|
ElementTypes,
|
||||||
createBlockStatement,
|
createBlockStatement,
|
||||||
CompilerOptions,
|
CompilerOptions,
|
||||||
isText
|
isText,
|
||||||
|
IfStatement,
|
||||||
|
CallExpression
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import { isString, escapeHtml, NO } from '@vue/shared'
|
import { isString, escapeHtml, NO } from '@vue/shared'
|
||||||
import { INTERPOLATE } from './runtimeHelpers'
|
import { INTERPOLATE } from './runtimeHelpers'
|
||||||
|
import { processIf } from './transforms/ssrVIf'
|
||||||
|
|
||||||
// Because SSR codegen output is completely different from client-side output
|
// Because SSR codegen output is completely different from client-side output
|
||||||
// (e.g. multiple elements can be concatenated into a single template literal
|
// (e.g. multiple elements can be concatenated into a single template literal
|
||||||
@ -37,22 +39,19 @@ export function ssrCodegenTransform(ast: RootNode, options: CompilerOptions) {
|
|||||||
ast.codegenNode = createBlockStatement(context.body)
|
ast.codegenNode = createBlockStatement(context.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SSRTransformContext = ReturnType<typeof createSSRTransformContext>
|
export type SSRTransformContext = ReturnType<typeof createSSRTransformContext>
|
||||||
|
|
||||||
function createSSRTransformContext(options: CompilerOptions) {
|
export function createSSRTransformContext(options: CompilerOptions) {
|
||||||
const body: BlockStatement['body'] = []
|
const body: BlockStatement['body'] = []
|
||||||
let currentCall: CallExpression | null = null
|
|
||||||
let currentString: TemplateLiteral | null = null
|
let currentString: TemplateLiteral | null = null
|
||||||
|
|
||||||
return {
|
return {
|
||||||
options,
|
options,
|
||||||
body,
|
body,
|
||||||
pushStringPart(part: TemplateLiteral['elements'][0]) {
|
pushStringPart(part: TemplateLiteral['elements'][0]) {
|
||||||
if (!currentCall) {
|
|
||||||
currentCall = createCallExpression(`_push`)
|
|
||||||
body.push(currentCall)
|
|
||||||
}
|
|
||||||
if (!currentString) {
|
if (!currentString) {
|
||||||
|
const currentCall = createCallExpression(`_push`)
|
||||||
|
body.push(currentCall)
|
||||||
currentString = createTemplateLiteral([])
|
currentString = createTemplateLiteral([])
|
||||||
currentCall.arguments.push(currentString)
|
currentCall.arguments.push(currentString)
|
||||||
}
|
}
|
||||||
@ -63,11 +62,16 @@ function createSSRTransformContext(options: CompilerOptions) {
|
|||||||
} else {
|
} else {
|
||||||
bufferedElements.push(part)
|
bufferedElements.push(part)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
pushStatement(statement: IfStatement | CallExpression) {
|
||||||
|
// close current string
|
||||||
|
currentString = null
|
||||||
|
body.push(statement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processChildren(
|
export function processChildren(
|
||||||
children: TemplateChildNode[],
|
children: TemplateChildNode[],
|
||||||
context: SSRTransformContext
|
context: SSRTransformContext
|
||||||
) {
|
) {
|
||||||
@ -98,7 +102,7 @@ function processChildren(
|
|||||||
} else if (child.type === NodeTypes.INTERPOLATION) {
|
} else if (child.type === NodeTypes.INTERPOLATION) {
|
||||||
context.pushStringPart(createCallExpression(INTERPOLATE, [child.content]))
|
context.pushStringPart(createCallExpression(INTERPOLATE, [child.content]))
|
||||||
} else if (child.type === NodeTypes.IF) {
|
} else if (child.type === NodeTypes.IF) {
|
||||||
// TODO
|
processIf(child, context)
|
||||||
} else if (child.type === NodeTypes.FOR) {
|
} else if (child.type === NodeTypes.FOR) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
@ -8,33 +8,6 @@ import {
|
|||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import { escapeHtml } from '@vue/shared'
|
import { escapeHtml } from '@vue/shared'
|
||||||
|
|
||||||
/*
|
|
||||||
## Simple Element
|
|
||||||
|
|
||||||
``` html
|
|
||||||
<div></div>
|
|
||||||
```
|
|
||||||
``` js
|
|
||||||
return function render(_ctx, _push, _parent) {
|
|
||||||
_push(`<div></div>`)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Consecutive Elements
|
|
||||||
|
|
||||||
``` html
|
|
||||||
<div>
|
|
||||||
<span></span>
|
|
||||||
</div>
|
|
||||||
<div></div>
|
|
||||||
```
|
|
||||||
``` js
|
|
||||||
return function render(_ctx, _push, _parent) {
|
|
||||||
_push(`<div><span></span></div><div></div>`)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const ssrTransformElement: NodeTransform = (node, context) => {
|
export const ssrTransformElement: NodeTransform = (node, context) => {
|
||||||
if (
|
if (
|
||||||
node.type === NodeTypes.ELEMENT &&
|
node.type === NodeTypes.ELEMENT &&
|
||||||
|
@ -1,3 +1,76 @@
|
|||||||
import { NodeTransform } from '@vue/compiler-dom'
|
import {
|
||||||
|
createStructuralDirectiveTransform,
|
||||||
|
processIfBranches,
|
||||||
|
IfNode,
|
||||||
|
createIfStatement,
|
||||||
|
createBlockStatement,
|
||||||
|
createCallExpression,
|
||||||
|
IfBranchNode,
|
||||||
|
BlockStatement,
|
||||||
|
NodeTypes
|
||||||
|
} from '@vue/compiler-dom'
|
||||||
|
import {
|
||||||
|
SSRTransformContext,
|
||||||
|
createSSRTransformContext,
|
||||||
|
processChildren
|
||||||
|
} from '../ssrCodegenTransform'
|
||||||
|
|
||||||
export const ssrTransformIf: NodeTransform = () => {}
|
// This is the plugin for the first transform pass, which simply constructs the
|
||||||
|
// if node and its branches.
|
||||||
|
export const ssrTransformIf = createStructuralDirectiveTransform(
|
||||||
|
/^(if|else|else-if)$/,
|
||||||
|
processIfBranches
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is called during the 2nd transform pass to construct the SSR-sepcific
|
||||||
|
// codegen nodes.
|
||||||
|
export function processIf(node: IfNode, context: SSRTransformContext) {
|
||||||
|
const [rootBranch] = node.branches
|
||||||
|
const ifStatement = createIfStatement(
|
||||||
|
rootBranch.condition!,
|
||||||
|
processIfBranch(rootBranch, context)
|
||||||
|
)
|
||||||
|
context.pushStatement(ifStatement)
|
||||||
|
|
||||||
|
let currentIf = ifStatement
|
||||||
|
for (let i = 1; i < node.branches.length; i++) {
|
||||||
|
const branch = node.branches[i]
|
||||||
|
const branchBlockStatement = processIfBranch(branch, context)
|
||||||
|
if (branch.condition) {
|
||||||
|
// else-if
|
||||||
|
currentIf = currentIf.alternate = createIfStatement(
|
||||||
|
branch.condition,
|
||||||
|
branchBlockStatement
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// else
|
||||||
|
currentIf.alternate = branchBlockStatement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentIf.alternate) {
|
||||||
|
currentIf.alternate = createBlockStatement([
|
||||||
|
createCallExpression(`_push`, ['`<!---->`'])
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function processIfBranch(
|
||||||
|
branch: IfBranchNode,
|
||||||
|
context: SSRTransformContext
|
||||||
|
): BlockStatement {
|
||||||
|
const { children } = branch
|
||||||
|
const firstChild = children[0]
|
||||||
|
// TODO optimize away nested fragments when the only child is a ForNode
|
||||||
|
const needFragmentWrapper =
|
||||||
|
children.length !== 1 || firstChild.type !== NodeTypes.ELEMENT
|
||||||
|
const childContext = createSSRTransformContext(context.options)
|
||||||
|
if (needFragmentWrapper) {
|
||||||
|
childContext.pushStringPart(`<!---->`)
|
||||||
|
}
|
||||||
|
processChildren(branch.children, childContext)
|
||||||
|
if (needFragmentWrapper) {
|
||||||
|
childContext.pushStringPart(`<!---->`)
|
||||||
|
}
|
||||||
|
return createBlockStatement(childContext.body)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user