mirror of https://github.com/vuejs/core.git
feat(compiler-vapor): `v-else` / `v-else-if` (#98)
Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
This commit is contained in:
parent
63aacf6194
commit
63a127b612
|
@ -20,6 +20,30 @@ export function render(_ctx) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: v-if > comment between branches 1`] = `
|
||||||
|
"import { template as _template, fragment as _fragment, createIf as _createIf, prepend as _prepend } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const t1 = _template("<!--foo--><p></p>")
|
||||||
|
const t2 = _template("<!--bar-->fine")
|
||||||
|
const t3 = _fragment()
|
||||||
|
const n0 = t3()
|
||||||
|
const n1 = _createIf(() => (_ctx.ok), () => {
|
||||||
|
const n2 = t0()
|
||||||
|
return n2
|
||||||
|
}, () => _createIf(() => (_ctx.orNot), () => {
|
||||||
|
const n3 = t1()
|
||||||
|
return n3
|
||||||
|
}, () => {
|
||||||
|
const n4 = t2()
|
||||||
|
return n4
|
||||||
|
}))
|
||||||
|
_prepend(n0, n1)
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compiler: v-if > dedupe same template 1`] = `
|
exports[`compiler: v-if > dedupe same template 1`] = `
|
||||||
"import { template as _template, fragment as _fragment, createIf as _createIf, append as _append } from 'vue/vapor';
|
"import { template as _template, fragment as _fragment, createIf as _createIf, append as _append } from 'vue/vapor';
|
||||||
|
|
||||||
|
@ -59,3 +83,67 @@ export function render(_ctx) {
|
||||||
return n0
|
return n0
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: v-if > v-if + v-else 1`] = `
|
||||||
|
"import { template as _template, fragment as _fragment, createIf as _createIf, prepend as _prepend } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const t1 = _template("<p></p>")
|
||||||
|
const t2 = _fragment()
|
||||||
|
const n0 = t2()
|
||||||
|
const n1 = _createIf(() => (_ctx.ok), () => {
|
||||||
|
const n2 = t0()
|
||||||
|
return n2
|
||||||
|
}, () => {
|
||||||
|
const n3 = t1()
|
||||||
|
return n3
|
||||||
|
})
|
||||||
|
_prepend(n0, n1)
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: v-if > v-if + v-else-if + v-else 1`] = `
|
||||||
|
"import { template as _template, fragment as _fragment, createIf as _createIf, prepend as _prepend } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const t1 = _template("<p></p>")
|
||||||
|
const t2 = _template("fine")
|
||||||
|
const t3 = _fragment()
|
||||||
|
const n0 = t3()
|
||||||
|
const n1 = _createIf(() => (_ctx.ok), () => {
|
||||||
|
const n2 = t0()
|
||||||
|
return n2
|
||||||
|
}, () => _createIf(() => (_ctx.orNot), () => {
|
||||||
|
const n3 = t1()
|
||||||
|
return n3
|
||||||
|
}, () => {
|
||||||
|
const n4 = t2()
|
||||||
|
return n4
|
||||||
|
}))
|
||||||
|
_prepend(n0, n1)
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: v-if > v-if + v-else-if 1`] = `
|
||||||
|
"import { template as _template, fragment as _fragment, createIf as _createIf, prepend as _prepend } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const t1 = _template("<p></p>")
|
||||||
|
const t2 = _fragment()
|
||||||
|
const n0 = t2()
|
||||||
|
const n1 = _createIf(() => (_ctx.ok), () => {
|
||||||
|
const n2 = t0()
|
||||||
|
return n2
|
||||||
|
}, () => _createIf(() => (_ctx.orNot), () => {
|
||||||
|
const n3 = t1()
|
||||||
|
return n3
|
||||||
|
}))
|
||||||
|
_prepend(n0, n1)
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import { makeCompile } from './_utils'
|
import { makeCompile } from './_utils'
|
||||||
import {
|
import {
|
||||||
|
IRNodeTypes,
|
||||||
|
type IfIRNode,
|
||||||
transformElement,
|
transformElement,
|
||||||
transformInterpolation,
|
transformInterpolation,
|
||||||
transformOnce,
|
transformOnce,
|
||||||
transformVIf,
|
transformVIf,
|
||||||
transformVText,
|
transformVText,
|
||||||
} from '../../src'
|
} from '../../src'
|
||||||
|
import { NodeTypes } from '@vue/compiler-core'
|
||||||
|
|
||||||
const compileWithVIf = makeCompile({
|
const compileWithVIf = makeCompile({
|
||||||
nodeTransforms: [
|
nodeTransforms: [
|
||||||
|
@ -21,15 +24,87 @@ const compileWithVIf = makeCompile({
|
||||||
|
|
||||||
describe('compiler: v-if', () => {
|
describe('compiler: v-if', () => {
|
||||||
test('basic v-if', () => {
|
test('basic v-if', () => {
|
||||||
const { code } = compileWithVIf(`<div v-if="ok">{{msg}}</div>`)
|
const { code, vaporHelpers, ir, helpers } = compileWithVIf(
|
||||||
|
`<div v-if="ok">{{msg}}</div>`,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(vaporHelpers).contains('createIf')
|
||||||
|
expect(helpers.size).toBe(0)
|
||||||
|
|
||||||
|
expect(ir.template).lengthOf(2)
|
||||||
|
expect(ir.template).toMatchObject([
|
||||||
|
{
|
||||||
|
template: '<div></div>',
|
||||||
|
type: IRNodeTypes.TEMPLATE_FACTORY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.FRAGMENT_FACTORY,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
expect(ir.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.IF,
|
||||||
|
id: 1,
|
||||||
|
condition: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: 'ok',
|
||||||
|
isStatic: false,
|
||||||
|
},
|
||||||
|
positive: {
|
||||||
|
type: IRNodeTypes.BLOCK_FUNCTION,
|
||||||
|
templateIndex: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.APPEND_NODE,
|
||||||
|
elements: [1],
|
||||||
|
parent: 0,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(ir.dynamic).toMatchObject({
|
||||||
|
id: 0,
|
||||||
|
children: { 0: { id: 1 } },
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(ir.effect).toEqual([])
|
||||||
|
expect((ir.operation[0] as IfIRNode).positive.effect).lengthOf(1)
|
||||||
|
|
||||||
expect(code).matchSnapshot()
|
expect(code).matchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('template v-if', () => {
|
test('template v-if', () => {
|
||||||
const { code } = compileWithVIf(
|
const { code, ir } = compileWithVIf(
|
||||||
`<template v-if="ok"><div/>hello<p v-text="msg"/></template>`,
|
`<template v-if="ok"><div/>hello<p v-text="msg"/></template>`,
|
||||||
)
|
)
|
||||||
expect(code).matchSnapshot()
|
expect(code).matchSnapshot()
|
||||||
|
|
||||||
|
expect(ir.template).lengthOf(2)
|
||||||
|
expect(ir.template[0]).toMatchObject({
|
||||||
|
template: '<div></div>hello<p></p>',
|
||||||
|
type: IRNodeTypes.TEMPLATE_FACTORY,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(ir.effect).toEqual([])
|
||||||
|
expect((ir.operation[0] as IfIRNode).positive.effect).toMatchObject([
|
||||||
|
{
|
||||||
|
operations: [
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.SET_TEXT,
|
||||||
|
element: 3,
|
||||||
|
value: {
|
||||||
|
content: 'msg',
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
isStatic: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
expect((ir.operation[0] as IfIRNode).positive.dynamic).toMatchObject({
|
||||||
|
id: 2,
|
||||||
|
children: { 2: { id: 3 } },
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('dedupe same template', () => {
|
test('dedupe same template', () => {
|
||||||
|
@ -42,10 +117,185 @@ describe('compiler: v-if', () => {
|
||||||
|
|
||||||
test.todo('v-if with v-once')
|
test.todo('v-if with v-once')
|
||||||
test.todo('component v-if')
|
test.todo('component v-if')
|
||||||
test.todo('v-if + v-else')
|
|
||||||
test.todo('v-if + v-else-if')
|
test('v-if + v-else', () => {
|
||||||
test.todo('v-if + v-else-if + v-else')
|
const { code, ir, vaporHelpers, helpers } = compileWithVIf(
|
||||||
test.todo('comment between branches')
|
`<div v-if="ok"/><p v-else/>`,
|
||||||
|
)
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
expect(ir.template).lengthOf(3)
|
||||||
|
expect(ir.template).toMatchObject([
|
||||||
|
{
|
||||||
|
template: '<div></div>',
|
||||||
|
type: IRNodeTypes.TEMPLATE_FACTORY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
template: '<p></p>',
|
||||||
|
type: IRNodeTypes.TEMPLATE_FACTORY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.FRAGMENT_FACTORY,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(vaporHelpers).contains('createIf')
|
||||||
|
expect(ir.effect).lengthOf(0)
|
||||||
|
expect(helpers).lengthOf(0)
|
||||||
|
expect(ir.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.IF,
|
||||||
|
id: 1,
|
||||||
|
condition: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: 'ok',
|
||||||
|
isStatic: false,
|
||||||
|
},
|
||||||
|
positive: {
|
||||||
|
type: IRNodeTypes.BLOCK_FUNCTION,
|
||||||
|
templateIndex: 0,
|
||||||
|
},
|
||||||
|
negative: {
|
||||||
|
type: IRNodeTypes.BLOCK_FUNCTION,
|
||||||
|
templateIndex: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.PREPEND_NODE,
|
||||||
|
elements: [1],
|
||||||
|
parent: 0,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('v-if + v-else-if', () => {
|
||||||
|
const { code, ir } = compileWithVIf(
|
||||||
|
`<div v-if="ok"/><p v-else-if="orNot"/>`,
|
||||||
|
)
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
expect(ir.template).lengthOf(3)
|
||||||
|
expect(ir.template).toMatchObject([
|
||||||
|
{
|
||||||
|
template: '<div></div>',
|
||||||
|
type: IRNodeTypes.TEMPLATE_FACTORY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
template: '<p></p>',
|
||||||
|
type: IRNodeTypes.TEMPLATE_FACTORY,
|
||||||
|
},
|
||||||
|
{ type: IRNodeTypes.FRAGMENT_FACTORY },
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(ir.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.IF,
|
||||||
|
id: 1,
|
||||||
|
condition: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: 'ok',
|
||||||
|
isStatic: false,
|
||||||
|
},
|
||||||
|
positive: {
|
||||||
|
type: IRNodeTypes.BLOCK_FUNCTION,
|
||||||
|
templateIndex: 0,
|
||||||
|
},
|
||||||
|
negative: {
|
||||||
|
type: IRNodeTypes.IF,
|
||||||
|
condition: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: 'orNot',
|
||||||
|
isStatic: false,
|
||||||
|
},
|
||||||
|
positive: {
|
||||||
|
type: IRNodeTypes.BLOCK_FUNCTION,
|
||||||
|
templateIndex: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.PREPEND_NODE,
|
||||||
|
elements: [1],
|
||||||
|
parent: 0,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('v-if + v-else-if + v-else', () => {
|
||||||
|
const { code, ir } = compileWithVIf(
|
||||||
|
`<div v-if="ok"/><p v-else-if="orNot"/><template v-else>fine</template>`,
|
||||||
|
)
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
expect(ir.template).lengthOf(4)
|
||||||
|
expect(ir.template).toMatchObject([
|
||||||
|
{
|
||||||
|
template: '<div></div>',
|
||||||
|
type: IRNodeTypes.TEMPLATE_FACTORY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
template: '<p></p>',
|
||||||
|
type: IRNodeTypes.TEMPLATE_FACTORY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
template: 'fine',
|
||||||
|
type: IRNodeTypes.TEMPLATE_FACTORY,
|
||||||
|
},
|
||||||
|
{ type: IRNodeTypes.FRAGMENT_FACTORY },
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(ir.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.IF,
|
||||||
|
id: 1,
|
||||||
|
positive: {
|
||||||
|
type: IRNodeTypes.BLOCK_FUNCTION,
|
||||||
|
templateIndex: 0,
|
||||||
|
},
|
||||||
|
negative: {
|
||||||
|
type: IRNodeTypes.IF,
|
||||||
|
positive: {
|
||||||
|
type: IRNodeTypes.BLOCK_FUNCTION,
|
||||||
|
templateIndex: 1,
|
||||||
|
},
|
||||||
|
negative: {
|
||||||
|
type: IRNodeTypes.BLOCK_FUNCTION,
|
||||||
|
templateIndex: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.PREPEND_NODE,
|
||||||
|
elements: [1],
|
||||||
|
parent: 0,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('comment between branches', () => {
|
||||||
|
const { code, ir } = compileWithVIf(`
|
||||||
|
<div v-if="ok"/>
|
||||||
|
<!--foo-->
|
||||||
|
<p v-else-if="orNot"/>
|
||||||
|
<!--bar-->
|
||||||
|
<template v-else>fine</template>
|
||||||
|
`)
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
expect(ir.template).lengthOf(4)
|
||||||
|
expect(ir.template).toMatchObject([
|
||||||
|
{
|
||||||
|
template: '<div></div>',
|
||||||
|
type: IRNodeTypes.TEMPLATE_FACTORY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
template: '<!--foo--><p></p>',
|
||||||
|
type: IRNodeTypes.TEMPLATE_FACTORY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
template: '<!--bar-->fine',
|
||||||
|
type: IRNodeTypes.TEMPLATE_FACTORY,
|
||||||
|
},
|
||||||
|
{ type: IRNodeTypes.FRAGMENT_FACTORY },
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
describe.todo('errors')
|
describe.todo('errors')
|
||||||
describe.todo('codegen')
|
describe.todo('codegen')
|
||||||
test.todo('v-on with v-if')
|
test.todo('v-on with v-if')
|
||||||
|
|
|
@ -1,12 +1,30 @@
|
||||||
import { type CodegenContext, genBlockFunctionContent } from '../generate'
|
import { type CodegenContext, genBlockFunctionContent } from '../generate'
|
||||||
import type { BlockFunctionIRNode, IfIRNode } from '../ir'
|
import { type BlockFunctionIRNode, IRNodeTypes, type IfIRNode } from '../ir'
|
||||||
import { genExpression } from './expression'
|
import { genExpression } from './expression'
|
||||||
|
|
||||||
export function genIf(oper: IfIRNode, context: CodegenContext) {
|
export function genIf(
|
||||||
const { pushFnCall, vaporHelper, pushNewline, push, withIndent } = context
|
oper: IfIRNode,
|
||||||
|
context: CodegenContext,
|
||||||
|
isNested = false,
|
||||||
|
) {
|
||||||
|
const { pushFnCall, vaporHelper, pushNewline, push } = context
|
||||||
const { condition, positive, negative } = oper
|
const { condition, positive, negative } = oper
|
||||||
|
|
||||||
pushNewline(`const n${oper.id} = `)
|
let positiveArg = () => genBlockFunction(positive, context)
|
||||||
|
let negativeArg: false | (() => void) = false
|
||||||
|
|
||||||
|
if (negative) {
|
||||||
|
if (negative.type === IRNodeTypes.BLOCK_FUNCTION) {
|
||||||
|
negativeArg = () => genBlockFunction(negative, context)
|
||||||
|
} else {
|
||||||
|
negativeArg = () => {
|
||||||
|
push('() => ')
|
||||||
|
genIf(negative!, context, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNested) pushNewline(`const n${oper.id} = `)
|
||||||
pushFnCall(
|
pushFnCall(
|
||||||
vaporHelper('createIf'),
|
vaporHelper('createIf'),
|
||||||
() => {
|
() => {
|
||||||
|
@ -14,15 +32,17 @@ export function genIf(oper: IfIRNode, context: CodegenContext) {
|
||||||
genExpression(condition, context)
|
genExpression(condition, context)
|
||||||
push(')')
|
push(')')
|
||||||
},
|
},
|
||||||
() => genBlockFunction(positive),
|
positiveArg,
|
||||||
!!negative && (() => genBlockFunction(negative!)),
|
negativeArg,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function genBlockFunction(oper: BlockFunctionIRNode, context: CodegenContext) {
|
||||||
|
const { pushNewline, push, withIndent } = context
|
||||||
|
|
||||||
function genBlockFunction(oper: BlockFunctionIRNode) {
|
|
||||||
push('() => {')
|
push('() => {')
|
||||||
withIndent(() => {
|
withIndent(() => {
|
||||||
genBlockFunctionContent(oper, context)
|
genBlockFunctionContent(oper, context)
|
||||||
})
|
})
|
||||||
pushNewline('}')
|
pushNewline('}')
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ export interface IfIRNode extends BaseIRNode {
|
||||||
id: number
|
id: number
|
||||||
condition: IRExpression
|
condition: IRExpression
|
||||||
positive: BlockFunctionIRNode
|
positive: BlockFunctionIRNode
|
||||||
negative?: BlockFunctionIRNode
|
negative?: BlockFunctionIRNode | IfIRNode
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TemplateFactoryIRNode extends BaseIRNode {
|
export interface TemplateFactoryIRNode extends BaseIRNode {
|
||||||
|
|
|
@ -42,9 +42,9 @@ export type DirectiveTransform = (
|
||||||
// A structural directive transform is technically also a NodeTransform;
|
// A structural directive transform is technically also a NodeTransform;
|
||||||
// Only v-if and v-for fall into this category.
|
// Only v-if and v-for fall into this category.
|
||||||
export type StructuralDirectiveTransform = (
|
export type StructuralDirectiveTransform = (
|
||||||
node: RootNode | TemplateChildNode,
|
node: ElementNode,
|
||||||
dir: VaporDirectiveNode,
|
dir: VaporDirectiveNode,
|
||||||
context: TransformContext<RootNode | TemplateChildNode>,
|
context: TransformContext<ElementNode>,
|
||||||
) => void | (() => void)
|
) => void | (() => void)
|
||||||
|
|
||||||
export type TransformOptions = HackOptions<BaseTransformOptions>
|
export type TransformOptions = HackOptions<BaseTransformOptions>
|
||||||
|
@ -60,7 +60,7 @@ export interface TransformContext<T extends AllNode = AllNode> {
|
||||||
>
|
>
|
||||||
|
|
||||||
template: string
|
template: string
|
||||||
childrenTemplate: string[]
|
childrenTemplate: (string | null)[]
|
||||||
dynamic: IRDynamicInfo
|
dynamic: IRDynamicInfo
|
||||||
|
|
||||||
inVOnce: boolean
|
inVOnce: boolean
|
||||||
|
@ -311,15 +311,12 @@ function transformNode(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.node.type === NodeTypes.ROOT)
|
if (context.node.type === NodeTypes.ROOT)
|
||||||
context.template += context.childrenTemplate.join('')
|
context.template += context.childrenTemplate.filter(Boolean).join('')
|
||||||
}
|
}
|
||||||
|
|
||||||
function transformChildren(ctx: TransformContext<RootNode | ElementNode>) {
|
function transformChildren(ctx: TransformContext<RootNode | ElementNode>) {
|
||||||
const { children } = ctx.node
|
const { children } = ctx.node
|
||||||
let i = 0
|
let i = 0
|
||||||
// const nodeRemoved = () => {
|
|
||||||
// i--
|
|
||||||
// }
|
|
||||||
for (; i < children.length; i++) {
|
for (; i < children.length; i++) {
|
||||||
const child = children[i]
|
const child = children[i]
|
||||||
const childContext = createContext(child, ctx, i)
|
const childContext = createContext(child, ctx, i)
|
||||||
|
@ -405,7 +402,11 @@ export function createStructuralDirectiveTransform(
|
||||||
const exitFns = []
|
const exitFns = []
|
||||||
for (const prop of props) {
|
for (const prop of props) {
|
||||||
if (prop.type === NodeTypes.DIRECTIVE && matches(prop.name)) {
|
if (prop.type === NodeTypes.DIRECTIVE && matches(prop.name)) {
|
||||||
const onExit = fn(node, prop as VaporDirectiveNode, context)
|
const onExit = fn(
|
||||||
|
node,
|
||||||
|
prop as VaporDirectiveNode,
|
||||||
|
context as TransformContext<ElementNode>,
|
||||||
|
)
|
||||||
if (onExit) exitFns.push(onExit)
|
if (onExit) exitFns.push(onExit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
type ElementNode,
|
||||||
ElementTypes,
|
ElementTypes,
|
||||||
ErrorCodes,
|
ErrorCodes,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
|
@ -15,6 +16,7 @@ import {
|
||||||
import {
|
import {
|
||||||
type BlockFunctionIRNode,
|
type BlockFunctionIRNode,
|
||||||
IRNodeTypes,
|
IRNodeTypes,
|
||||||
|
type OperationNode,
|
||||||
type VaporDirectiveNode,
|
type VaporDirectiveNode,
|
||||||
} from '../ir'
|
} from '../ir'
|
||||||
import { extend } from '@vue/shared'
|
import { extend } from '@vue/shared'
|
||||||
|
@ -25,7 +27,7 @@ export const transformVIf = createStructuralDirectiveTransform(
|
||||||
)
|
)
|
||||||
|
|
||||||
export function processIf(
|
export function processIf(
|
||||||
node: RootNode | TemplateChildNode,
|
node: ElementNode,
|
||||||
dir: VaporDirectiveNode,
|
dir: VaporDirectiveNode,
|
||||||
context: TransformContext<RootNode | TemplateChildNode>,
|
context: TransformContext<RootNode | TemplateChildNode>,
|
||||||
) {
|
) {
|
||||||
|
@ -40,7 +42,7 @@ export function processIf(
|
||||||
if (dir.name === 'if') {
|
if (dir.name === 'if') {
|
||||||
const id = context.reference()
|
const id = context.reference()
|
||||||
context.dynamic.ghost = true
|
context.dynamic.ghost = true
|
||||||
const [branch, onExit] = createIfBranch(node, dir, context)
|
const [branch, onExit] = createIfBranch(node, context)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
onExit()
|
onExit()
|
||||||
|
@ -52,37 +54,100 @@ export function processIf(
|
||||||
positive: branch,
|
positive: branch,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// check the adjacent v-if
|
||||||
|
const parent = context.parent!
|
||||||
|
const siblings = parent.node.children
|
||||||
|
const templates = parent.childrenTemplate
|
||||||
|
|
||||||
|
const comments = []
|
||||||
|
let sibling: TemplateChildNode | undefined
|
||||||
|
let i = siblings.indexOf(node)
|
||||||
|
while (i-- >= -1) {
|
||||||
|
sibling = siblings[i]
|
||||||
|
|
||||||
|
if (sibling) {
|
||||||
|
if (sibling.type === NodeTypes.COMMENT) {
|
||||||
|
__DEV__ && comments.unshift(sibling)
|
||||||
|
templates[i] = null
|
||||||
|
continue
|
||||||
|
} else if (
|
||||||
|
sibling.type === NodeTypes.TEXT &&
|
||||||
|
!sibling.content.trim().length
|
||||||
|
) {
|
||||||
|
templates[i] = null
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
const { operation } = context.block
|
||||||
|
let lastIfNode: OperationNode
|
||||||
|
if (
|
||||||
|
// check if v-if is the sibling node
|
||||||
|
!sibling ||
|
||||||
|
sibling.type !== NodeTypes.ELEMENT ||
|
||||||
|
!sibling.props.some(
|
||||||
|
({ type, name }) =>
|
||||||
|
type === NodeTypes.DIRECTIVE && ['if', 'else-if'].includes(name),
|
||||||
|
) ||
|
||||||
|
// check if IFNode is the last operation and get the root IFNode
|
||||||
|
!(lastIfNode = operation[operation.length - 1]) ||
|
||||||
|
lastIfNode.type !== IRNodeTypes.IF
|
||||||
|
) {
|
||||||
|
context.options.onError(
|
||||||
|
createCompilerError(ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, node.loc),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
while (lastIfNode.negative && lastIfNode.negative.type === IRNodeTypes.IF) {
|
||||||
|
lastIfNode = lastIfNode.negative
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if v-else was followed by v-else-if
|
||||||
|
if (dir.name === 'else-if' && lastIfNode.negative) {
|
||||||
|
context.options.onError(
|
||||||
|
createCompilerError(ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, node.loc),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO ignore comments if the v-if is direct child of <transition> (PR #3622)
|
||||||
|
if (__DEV__ && comments.length) {
|
||||||
|
node = wrapTemplate(node)
|
||||||
|
context.node = node = extend({}, node, {
|
||||||
|
children: [...comments, ...node.children],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const [branch, onExit] = createIfBranch(node, context)
|
||||||
|
|
||||||
|
if (dir.name === 'else') {
|
||||||
|
lastIfNode.negative = branch
|
||||||
|
} else {
|
||||||
|
lastIfNode.negative = {
|
||||||
|
type: IRNodeTypes.IF,
|
||||||
|
id: -1,
|
||||||
|
loc: dir.loc,
|
||||||
|
condition: dir.exp!,
|
||||||
|
positive: branch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => onExit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createIfBranch(
|
export function createIfBranch(
|
||||||
node: RootNode | TemplateChildNode,
|
node: ElementNode,
|
||||||
dir: VaporDirectiveNode,
|
|
||||||
context: TransformContext<RootNode | TemplateChildNode>,
|
context: TransformContext<RootNode | TemplateChildNode>,
|
||||||
): [BlockFunctionIRNode, () => void] {
|
): [BlockFunctionIRNode, () => void] {
|
||||||
if (
|
context.node = node = wrapTemplate(node)
|
||||||
node.type === NodeTypes.ELEMENT &&
|
|
||||||
node.tagType !== ElementTypes.TEMPLATE
|
|
||||||
) {
|
|
||||||
node = extend({}, node, {
|
|
||||||
type: NodeTypes.ELEMENT,
|
|
||||||
tag: 'template',
|
|
||||||
props: [],
|
|
||||||
tagType: ElementTypes.TEMPLATE,
|
|
||||||
children: [
|
|
||||||
extend({}, node, {
|
|
||||||
props: node.props.filter(
|
|
||||||
p => p.type !== NodeTypes.DIRECTIVE && p.name !== 'if',
|
|
||||||
),
|
|
||||||
} as TemplateChildNode),
|
|
||||||
],
|
|
||||||
} as Partial<TemplateNode>)
|
|
||||||
context.node = node
|
|
||||||
}
|
|
||||||
|
|
||||||
const branch: BlockFunctionIRNode = {
|
const branch: BlockFunctionIRNode = {
|
||||||
type: IRNodeTypes.BLOCK_FUNCTION,
|
type: IRNodeTypes.BLOCK_FUNCTION,
|
||||||
loc: dir.loc,
|
loc: node.loc,
|
||||||
node,
|
node,
|
||||||
templateIndex: -1,
|
templateIndex: -1,
|
||||||
dynamic: {
|
dynamic: {
|
||||||
|
@ -105,3 +170,22 @@ export function createIfBranch(
|
||||||
}
|
}
|
||||||
return [branch, onExit]
|
return [branch, onExit]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wrapTemplate(node: ElementNode): TemplateNode {
|
||||||
|
if (node.tagType === ElementTypes.TEMPLATE) {
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
return extend({}, node, {
|
||||||
|
type: NodeTypes.ELEMENT,
|
||||||
|
tag: 'template',
|
||||||
|
props: [],
|
||||||
|
tagType: ElementTypes.TEMPLATE,
|
||||||
|
children: [
|
||||||
|
extend({}, node, {
|
||||||
|
props: node.props.filter(
|
||||||
|
p => p.type !== NodeTypes.DIRECTIVE && p.name !== 'if',
|
||||||
|
),
|
||||||
|
} as TemplateChildNode),
|
||||||
|
],
|
||||||
|
} as Partial<TemplateNode>)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue