test(compiler): tests for vIf codegen w/ blocks optimization
This commit is contained in:
parent
5de744d4e1
commit
ed111cd37b
@ -5,7 +5,7 @@ exports[`compiler: integration tests function mode 1`] = `
|
|||||||
|
|
||||||
return function render() {
|
return function render() {
|
||||||
with (this) {
|
with (this) {
|
||||||
const { createVNode: _createVNode, toString: _toString, openBlock: _openBlock, applyDirectives: _applyDirectives, createBlock: _createBlock, Empty: _Empty, Fragment: _Fragment, renderList: _renderList } = _Vue
|
const { createVNode: _createVNode, toString: _toString, openBlock: _openBlock, createBlock: _createBlock, Empty: _Empty, Fragment: _Fragment, renderList: _renderList } = _Vue
|
||||||
|
|
||||||
return _createVNode(\\"div\\", {
|
return _createVNode(\\"div\\", {
|
||||||
id: \\"foo\\",
|
id: \\"foo\\",
|
||||||
@ -13,16 +13,8 @@ return function render() {
|
|||||||
}, [
|
}, [
|
||||||
_toString(world.burn()),
|
_toString(world.burn()),
|
||||||
(_openBlock(), ok
|
(_openBlock(), ok
|
||||||
? _createBlock(
|
? _createBlock(\\"div\\", { key: 0 }, \\"yes\\")
|
||||||
\\"div\\",
|
: _createBlock(_Fragment, { key: 1 }, \\"no\\")),
|
||||||
{ key: 0 },
|
|
||||||
\\"yes\\"
|
|
||||||
)
|
|
||||||
: _createBlock(
|
|
||||||
_Fragment,
|
|
||||||
{ key: 1 },
|
|
||||||
\\"no\\"
|
|
||||||
)),
|
|
||||||
_renderList(list, (value, index) => {
|
_renderList(list, (value, index) => {
|
||||||
return _createVNode(\\"div\\", null, [
|
return _createVNode(\\"div\\", null, [
|
||||||
_createVNode(\\"span\\", null, _toString(value + index))
|
_createVNode(\\"span\\", null, _toString(value + index))
|
||||||
@ -34,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 { createVNode, toString, openBlock, applyDirectives, createBlock, Empty, Fragment, renderList } = Vue
|
"const { createVNode, toString, openBlock, createBlock, Empty, Fragment, renderList } = Vue
|
||||||
|
|
||||||
return function render() {
|
return function render() {
|
||||||
const _ctx = this
|
const _ctx = this
|
||||||
@ -44,16 +36,8 @@ return function render() {
|
|||||||
}, [
|
}, [
|
||||||
toString(_ctx.world.burn()),
|
toString(_ctx.world.burn()),
|
||||||
(openBlock(), (_ctx.ok)
|
(openBlock(), (_ctx.ok)
|
||||||
? createBlock(
|
? createBlock(\\"div\\", { key: 0 }, \\"yes\\")
|
||||||
\\"div\\",
|
: createBlock(Fragment, { key: 1 }, \\"no\\")),
|
||||||
{ key: 0 },
|
|
||||||
\\"yes\\"
|
|
||||||
)
|
|
||||||
: createBlock(
|
|
||||||
Fragment,
|
|
||||||
{ key: 1 },
|
|
||||||
\\"no\\"
|
|
||||||
)),
|
|
||||||
renderList(_ctx.list, (value, index) => {
|
renderList(_ctx.list, (value, index) => {
|
||||||
return createVNode(\\"div\\", null, [
|
return createVNode(\\"div\\", null, [
|
||||||
createVNode(\\"span\\", null, toString(value + index))
|
createVNode(\\"span\\", null, toString(value + index))
|
||||||
@ -64,7 +48,7 @@ return function render() {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compiler: integration tests module mode 1`] = `
|
exports[`compiler: integration tests module mode 1`] = `
|
||||||
"import { createVNode, toString, openBlock, applyDirectives, createBlock, Empty, Fragment, renderList } from \\"vue\\"
|
"import { createVNode, toString, openBlock, createBlock, Empty, Fragment, renderList } from \\"vue\\"
|
||||||
|
|
||||||
export default function render() {
|
export default function render() {
|
||||||
const _ctx = this
|
const _ctx = this
|
||||||
@ -74,16 +58,8 @@ export default function render() {
|
|||||||
}, [
|
}, [
|
||||||
_toString(_ctx.world.burn()),
|
_toString(_ctx.world.burn()),
|
||||||
(openBlock(), (_ctx.ok)
|
(openBlock(), (_ctx.ok)
|
||||||
? createBlock(
|
? createBlock(\\"div\\", { key: 0 }, \\"yes\\")
|
||||||
\\"div\\",
|
: createBlock(Fragment, { key: 1 }, \\"no\\")),
|
||||||
{ key: 0 },
|
|
||||||
\\"yes\\"
|
|
||||||
)
|
|
||||||
: createBlock(
|
|
||||||
Fragment,
|
|
||||||
{ key: 1 },
|
|
||||||
\\"no\\"
|
|
||||||
)),
|
|
||||||
_renderList(_ctx.list, (value, index) => {
|
_renderList(_ctx.list, (value, index) => {
|
||||||
return createVNode(\\"div\\", null, [
|
return createVNode(\\"div\\", null, [
|
||||||
createVNode(\\"span\\", null, _toString(value + index))
|
createVNode(\\"span\\", null, _toString(value + index))
|
||||||
|
28
packages/compiler-core/__tests__/testUtils.ts
Normal file
28
packages/compiler-core/__tests__/testUtils.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { NodeTypes } from '../src'
|
||||||
|
|
||||||
|
const leadingBracketRE = /^\[/
|
||||||
|
const bracketsRE = /^\[|\]$/g
|
||||||
|
|
||||||
|
// Create a matcher for an object
|
||||||
|
// where non-static expressions should be wrapped in []
|
||||||
|
// e.g.
|
||||||
|
// - createObjectMatcher({ 'foo': '[bar]' }) matches { foo: bar }
|
||||||
|
// - createObjectMatcher({ '[foo]': 'bar' }) matches { [foo]: "bar" }
|
||||||
|
export function createObjectMatcher(obj: any) {
|
||||||
|
return {
|
||||||
|
type: NodeTypes.JS_OBJECT_EXPRESSION,
|
||||||
|
properties: Object.keys(obj).map(key => ({
|
||||||
|
type: NodeTypes.JS_PROPERTY,
|
||||||
|
key: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: key.replace(bracketsRE, ''),
|
||||||
|
isStatic: !leadingBracketRE.test(key)
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: obj[key].replace(bracketsRE, ''),
|
||||||
|
isStatic: !leadingBracketRE.test(obj[key])
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`compiler: transform v-if codegen basic v-if 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, Empty: _Empty } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), ok
|
||||||
|
? _createBlock(\\"div\\", { key: 0 })
|
||||||
|
: _createBlock(_Empty))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform v-if codegen template v-if 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, Fragment: _Fragment, Empty: _Empty } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), ok
|
||||||
|
? _createBlock(_Fragment, { key: 0 }, [
|
||||||
|
_createVNode(\\"div\\"),
|
||||||
|
\\"hello\\",
|
||||||
|
_createVNode(\\"p\\")
|
||||||
|
])
|
||||||
|
: _createBlock(_Empty))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform v-if codegen v-if + v-else 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, Empty: _Empty } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), ok
|
||||||
|
? _createBlock(\\"div\\", { key: 0 })
|
||||||
|
: _createBlock(\\"p\\", { key: 1 }))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform v-if codegen v-if + v-else-if + v-else 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, Empty: _Empty, Fragment: _Fragment } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), ok
|
||||||
|
? _createBlock(\\"div\\", { key: 0 })
|
||||||
|
: orNot
|
||||||
|
? _createBlock(\\"p\\", { key: 1 })
|
||||||
|
: _createBlock(_Fragment, { key: 2 }, \\"fine\\"))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform v-if codegen v-if + v-else-if 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
return function render() {
|
||||||
|
with (this) {
|
||||||
|
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, Empty: _Empty } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), ok
|
||||||
|
? _createBlock(\\"div\\", { key: 0 })
|
||||||
|
: orNot
|
||||||
|
? _createBlock(\\"p\\", { key: 1 })
|
||||||
|
: _createBlock(_Empty))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
@ -25,6 +25,7 @@ import { transformOn } from '../../src/transforms/vOn'
|
|||||||
import { transformStyle } from '../../src/transforms/transformStyle'
|
import { transformStyle } from '../../src/transforms/transformStyle'
|
||||||
import { transformBind } from '../../src/transforms/vBind'
|
import { transformBind } from '../../src/transforms/vBind'
|
||||||
import { PatchFlags } from '@vue/shared'
|
import { PatchFlags } from '@vue/shared'
|
||||||
|
import { createObjectMatcher } from '../testUtils'
|
||||||
|
|
||||||
function parseWithElementTransform(
|
function parseWithElementTransform(
|
||||||
template: string,
|
template: string,
|
||||||
@ -47,25 +48,6 @@ function parseWithElementTransform(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createStaticObjectMatcher(obj: any) {
|
|
||||||
return {
|
|
||||||
type: NodeTypes.JS_OBJECT_EXPRESSION,
|
|
||||||
properties: Object.keys(obj).map(key => ({
|
|
||||||
type: NodeTypes.JS_PROPERTY,
|
|
||||||
key: {
|
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
||||||
content: key,
|
|
||||||
isStatic: true
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
||||||
content: obj[key],
|
|
||||||
isStatic: true
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('compiler: element transform', () => {
|
describe('compiler: element transform', () => {
|
||||||
test('import + resovle component', () => {
|
test('import + resovle component', () => {
|
||||||
const { root } = parseWithElementTransform(`<Foo/>`)
|
const { root } = parseWithElementTransform(`<Foo/>`)
|
||||||
@ -78,7 +60,7 @@ describe('compiler: element transform', () => {
|
|||||||
expect(node.callee).toBe(`_${CREATE_VNODE}`)
|
expect(node.callee).toBe(`_${CREATE_VNODE}`)
|
||||||
expect(node.arguments).toMatchObject([
|
expect(node.arguments).toMatchObject([
|
||||||
`"div"`,
|
`"div"`,
|
||||||
createStaticObjectMatcher({
|
createObjectMatcher({
|
||||||
id: 'foo',
|
id: 'foo',
|
||||||
class: 'bar'
|
class: 'bar'
|
||||||
})
|
})
|
||||||
@ -90,7 +72,7 @@ describe('compiler: element transform', () => {
|
|||||||
expect(node.callee).toBe(`_${CREATE_VNODE}`)
|
expect(node.callee).toBe(`_${CREATE_VNODE}`)
|
||||||
expect(node.arguments).toMatchObject([
|
expect(node.arguments).toMatchObject([
|
||||||
`"div"`,
|
`"div"`,
|
||||||
createStaticObjectMatcher({
|
createObjectMatcher({
|
||||||
id: 'foo'
|
id: 'foo'
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
@ -147,7 +129,7 @@ describe('compiler: element transform', () => {
|
|||||||
type: NodeTypes.JS_CALL_EXPRESSION,
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
callee: `_${MERGE_PROPS}`,
|
callee: `_${MERGE_PROPS}`,
|
||||||
arguments: [
|
arguments: [
|
||||||
createStaticObjectMatcher({
|
createObjectMatcher({
|
||||||
id: 'foo'
|
id: 'foo'
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@ -172,7 +154,7 @@ describe('compiler: element transform', () => {
|
|||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
content: `obj`
|
content: `obj`
|
||||||
},
|
},
|
||||||
createStaticObjectMatcher({
|
createObjectMatcher({
|
||||||
id: 'foo'
|
id: 'foo'
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
@ -189,14 +171,14 @@ describe('compiler: element transform', () => {
|
|||||||
type: NodeTypes.JS_CALL_EXPRESSION,
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
callee: `_${MERGE_PROPS}`,
|
callee: `_${MERGE_PROPS}`,
|
||||||
arguments: [
|
arguments: [
|
||||||
createStaticObjectMatcher({
|
createObjectMatcher({
|
||||||
id: 'foo'
|
id: 'foo'
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
content: `obj`
|
content: `obj`
|
||||||
},
|
},
|
||||||
createStaticObjectMatcher({
|
createObjectMatcher({
|
||||||
class: 'bar'
|
class: 'bar'
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
@ -213,7 +195,7 @@ describe('compiler: element transform', () => {
|
|||||||
type: NodeTypes.JS_CALL_EXPRESSION,
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
callee: `_${MERGE_PROPS}`,
|
callee: `_${MERGE_PROPS}`,
|
||||||
arguments: [
|
arguments: [
|
||||||
createStaticObjectMatcher({
|
createObjectMatcher({
|
||||||
id: 'foo'
|
id: 'foo'
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@ -226,7 +208,7 @@ describe('compiler: element transform', () => {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
createStaticObjectMatcher({
|
createObjectMatcher({
|
||||||
class: 'bar'
|
class: 'bar'
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
@ -243,7 +225,7 @@ describe('compiler: element transform', () => {
|
|||||||
type: NodeTypes.JS_CALL_EXPRESSION,
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
callee: `_${MERGE_PROPS}`,
|
callee: `_${MERGE_PROPS}`,
|
||||||
arguments: [
|
arguments: [
|
||||||
createStaticObjectMatcher({
|
createObjectMatcher({
|
||||||
id: 'foo'
|
id: 'foo'
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
|
@ -8,31 +8,46 @@ import {
|
|||||||
ElementNode,
|
ElementNode,
|
||||||
TextNode,
|
TextNode,
|
||||||
CommentNode,
|
CommentNode,
|
||||||
SimpleExpressionNode
|
SimpleExpressionNode,
|
||||||
|
SequenceExpression,
|
||||||
|
ConditionalExpression,
|
||||||
|
CallExpression
|
||||||
} from '../../src/ast'
|
} from '../../src/ast'
|
||||||
import { ErrorCodes } from '../../src/errors'
|
import { ErrorCodes } from '../../src/errors'
|
||||||
import { CompilerOptions } from '../../src'
|
import { CompilerOptions, generate } from '../../src'
|
||||||
|
import {
|
||||||
|
OPEN_BLOCK,
|
||||||
|
CREATE_BLOCK,
|
||||||
|
EMPTY,
|
||||||
|
FRAGMENT,
|
||||||
|
MERGE_PROPS,
|
||||||
|
APPLY_DIRECTIVES
|
||||||
|
} from '../../src/runtimeConstants'
|
||||||
|
import { createObjectMatcher } from '../testUtils'
|
||||||
|
|
||||||
function parseWithIfTransform(
|
function parseWithIfTransform(
|
||||||
template: string,
|
template: string,
|
||||||
options: CompilerOptions = {},
|
options: CompilerOptions = {},
|
||||||
returnIndex: number = 0
|
returnIndex: number = 0
|
||||||
): IfNode {
|
) {
|
||||||
const node = parse(template, options)
|
const ast = parse(template, options)
|
||||||
transform(node, {
|
transform(ast, {
|
||||||
nodeTransforms: [transformIf, transformElement],
|
nodeTransforms: [transformIf, transformElement],
|
||||||
...options
|
...options
|
||||||
})
|
})
|
||||||
if (!options.onError) {
|
if (!options.onError) {
|
||||||
expect(node.children.length).toBe(1)
|
expect(ast.children.length).toBe(1)
|
||||||
expect(node.children[0].type).toBe(NodeTypes.IF)
|
expect(ast.children[0].type).toBe(NodeTypes.IF)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
root: ast,
|
||||||
|
node: ast.children[returnIndex] as IfNode
|
||||||
}
|
}
|
||||||
return node.children[returnIndex] as IfNode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('compiler: transform v-if', () => {
|
describe('compiler: transform v-if', () => {
|
||||||
test('basic v-if', () => {
|
test('basic v-if', () => {
|
||||||
const node = parseWithIfTransform(`<div v-if="ok"/>`)
|
const { node } = parseWithIfTransform(`<div v-if="ok"/>`)
|
||||||
expect(node.type).toBe(NodeTypes.IF)
|
expect(node.type).toBe(NodeTypes.IF)
|
||||||
expect(node.branches.length).toBe(1)
|
expect(node.branches.length).toBe(1)
|
||||||
expect((node.branches[0].condition as SimpleExpressionNode).content).toBe(
|
expect((node.branches[0].condition as SimpleExpressionNode).content).toBe(
|
||||||
@ -44,7 +59,7 @@ describe('compiler: transform v-if', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('template v-if', () => {
|
test('template v-if', () => {
|
||||||
const node = parseWithIfTransform(
|
const { node } = parseWithIfTransform(
|
||||||
`<template v-if="ok"><div/>hello<p/></template>`
|
`<template v-if="ok"><div/>hello<p/></template>`
|
||||||
)
|
)
|
||||||
expect(node.type).toBe(NodeTypes.IF)
|
expect(node.type).toBe(NodeTypes.IF)
|
||||||
@ -62,7 +77,7 @@ describe('compiler: transform v-if', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('v-if + v-else', () => {
|
test('v-if + v-else', () => {
|
||||||
const node = parseWithIfTransform(`<div v-if="ok"/><p v-else/>`)
|
const { node } = parseWithIfTransform(`<div v-if="ok"/><p v-else/>`)
|
||||||
expect(node.type).toBe(NodeTypes.IF)
|
expect(node.type).toBe(NodeTypes.IF)
|
||||||
expect(node.branches.length).toBe(2)
|
expect(node.branches.length).toBe(2)
|
||||||
|
|
||||||
@ -80,7 +95,9 @@ describe('compiler: transform v-if', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('v-if + v-else-if', () => {
|
test('v-if + v-else-if', () => {
|
||||||
const node = parseWithIfTransform(`<div v-if="ok"/><p v-else-if="orNot"/>`)
|
const { node } = parseWithIfTransform(
|
||||||
|
`<div v-if="ok"/><p v-else-if="orNot"/>`
|
||||||
|
)
|
||||||
expect(node.type).toBe(NodeTypes.IF)
|
expect(node.type).toBe(NodeTypes.IF)
|
||||||
expect(node.branches.length).toBe(2)
|
expect(node.branches.length).toBe(2)
|
||||||
|
|
||||||
@ -98,7 +115,7 @@ describe('compiler: transform v-if', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('v-if + v-else-if + v-else', () => {
|
test('v-if + v-else-if + v-else', () => {
|
||||||
const node = parseWithIfTransform(
|
const { node } = parseWithIfTransform(
|
||||||
`<div v-if="ok"/><p v-else-if="orNot"/><template v-else>fine</template>`
|
`<div v-if="ok"/><p v-else-if="orNot"/><template v-else>fine</template>`
|
||||||
)
|
)
|
||||||
expect(node.type).toBe(NodeTypes.IF)
|
expect(node.type).toBe(NodeTypes.IF)
|
||||||
@ -124,7 +141,7 @@ describe('compiler: transform v-if', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('comment between branches', () => {
|
test('comment between branches', () => {
|
||||||
const node = parseWithIfTransform(`
|
const { node } = parseWithIfTransform(`
|
||||||
<div v-if="ok"/>
|
<div v-if="ok"/>
|
||||||
<!--foo-->
|
<!--foo-->
|
||||||
<p v-else-if="orNot"/>
|
<p v-else-if="orNot"/>
|
||||||
@ -158,24 +175,20 @@ describe('compiler: transform v-if', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('should prefix v-if condition', () => {
|
test('should prefix v-if condition', () => {
|
||||||
const node = parseWithIfTransform(`<div v-if="ok"/>`, {
|
const { node } = parseWithIfTransform(`<div v-if="ok"/>`, {
|
||||||
prefixIdentifiers: true
|
prefixIdentifiers: true
|
||||||
}) as IfNode
|
})
|
||||||
expect(node.branches[0].condition).toMatchObject({
|
expect(node.branches[0].condition).toMatchObject({
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
content: `_ctx.ok`
|
content: `_ctx.ok`
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('codegen', () => {
|
|
||||||
// TODO
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('errors', () => {
|
describe('errors', () => {
|
||||||
test('error on v-else missing adjacent v-if', () => {
|
test('error on v-else missing adjacent v-if', () => {
|
||||||
const onError = jest.fn()
|
const onError = jest.fn()
|
||||||
|
|
||||||
const node1 = parseWithIfTransform(`<div v-else/>`, { onError })
|
const { node: node1 } = parseWithIfTransform(`<div v-else/>`, { onError })
|
||||||
expect(onError.mock.calls[0]).toMatchObject([
|
expect(onError.mock.calls[0]).toMatchObject([
|
||||||
{
|
{
|
||||||
code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
|
code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
|
||||||
@ -183,7 +196,11 @@ describe('compiler: transform v-if', () => {
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
const node2 = parseWithIfTransform(`<div/><div v-else/>`, { onError }, 1)
|
const { node: node2 } = parseWithIfTransform(
|
||||||
|
`<div/><div v-else/>`,
|
||||||
|
{ onError },
|
||||||
|
1
|
||||||
|
)
|
||||||
expect(onError.mock.calls[1]).toMatchObject([
|
expect(onError.mock.calls[1]).toMatchObject([
|
||||||
{
|
{
|
||||||
code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
|
code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
|
||||||
@ -191,7 +208,7 @@ describe('compiler: transform v-if', () => {
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
const node3 = parseWithIfTransform(
|
const { node: node3 } = parseWithIfTransform(
|
||||||
`<div/>foo<div v-else/>`,
|
`<div/>foo<div v-else/>`,
|
||||||
{ onError },
|
{ onError },
|
||||||
2
|
2
|
||||||
@ -207,7 +224,9 @@ describe('compiler: transform v-if', () => {
|
|||||||
test('error on v-else-if missing adjacent v-if', () => {
|
test('error on v-else-if missing adjacent v-if', () => {
|
||||||
const onError = jest.fn()
|
const onError = jest.fn()
|
||||||
|
|
||||||
const node1 = parseWithIfTransform(`<div v-else-if="foo"/>`, { onError })
|
const { node: node1 } = parseWithIfTransform(`<div v-else-if="foo"/>`, {
|
||||||
|
onError
|
||||||
|
})
|
||||||
expect(onError.mock.calls[0]).toMatchObject([
|
expect(onError.mock.calls[0]).toMatchObject([
|
||||||
{
|
{
|
||||||
code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
|
code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
|
||||||
@ -215,7 +234,7 @@ describe('compiler: transform v-if', () => {
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
const node2 = parseWithIfTransform(
|
const { node: node2 } = parseWithIfTransform(
|
||||||
`<div/><div v-else-if="foo"/>`,
|
`<div/><div v-else-if="foo"/>`,
|
||||||
{ onError },
|
{ onError },
|
||||||
1
|
1
|
||||||
@ -227,7 +246,7 @@ describe('compiler: transform v-if', () => {
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
const node3 = parseWithIfTransform(
|
const { node: node3 } = parseWithIfTransform(
|
||||||
`<div/>foo<div v-else-if="foo"/>`,
|
`<div/>foo<div v-else-if="foo"/>`,
|
||||||
{ onError },
|
{ onError },
|
||||||
2
|
2
|
||||||
@ -240,4 +259,214 @@ describe('compiler: transform v-if', () => {
|
|||||||
])
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('codegen', () => {
|
||||||
|
function assertSharedCodegen(node: SequenceExpression, depth: number = 0) {
|
||||||
|
expect(node).toMatchObject({
|
||||||
|
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
|
||||||
|
expressions: [
|
||||||
|
{
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
callee: `_${OPEN_BLOCK}`,
|
||||||
|
arguments: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NodeTypes.JS_CONDITIONAL_EXPRESSION,
|
||||||
|
test: {
|
||||||
|
content: `ok`
|
||||||
|
},
|
||||||
|
consequent: {
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
callee: `_${CREATE_BLOCK}`
|
||||||
|
},
|
||||||
|
alternate:
|
||||||
|
depth < 1
|
||||||
|
? {
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
callee: `_${CREATE_BLOCK}`
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
type: NodeTypes.JS_CONDITIONAL_EXPRESSION,
|
||||||
|
test: {
|
||||||
|
content: `orNot`
|
||||||
|
},
|
||||||
|
consequent: {
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
callee: `_${CREATE_BLOCK}`
|
||||||
|
},
|
||||||
|
alternate: {
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
callee: `_${CREATE_BLOCK}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
test('basic v-if', () => {
|
||||||
|
const {
|
||||||
|
root,
|
||||||
|
node: { codegenNode }
|
||||||
|
} = parseWithIfTransform(`<div v-if="ok"/>`)
|
||||||
|
assertSharedCodegen(codegenNode)
|
||||||
|
const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
|
||||||
|
.consequent as CallExpression
|
||||||
|
expect(branch1.arguments).toMatchObject([`"div"`, `{ key: 0 }`])
|
||||||
|
const branch2 = (codegenNode.expressions[1] as ConditionalExpression)
|
||||||
|
.alternate as CallExpression
|
||||||
|
expect(branch2.arguments).toMatchObject([`_${EMPTY}`])
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('template v-if', () => {
|
||||||
|
const {
|
||||||
|
root,
|
||||||
|
node: { codegenNode }
|
||||||
|
} = parseWithIfTransform(`<template v-if="ok"><div/>hello<p/></template>`)
|
||||||
|
assertSharedCodegen(codegenNode)
|
||||||
|
const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
|
||||||
|
.consequent as CallExpression
|
||||||
|
expect(branch1.arguments).toMatchObject([
|
||||||
|
`_${FRAGMENT}`,
|
||||||
|
`{ key: 0 }`,
|
||||||
|
[
|
||||||
|
{ type: NodeTypes.ELEMENT, tag: 'div' },
|
||||||
|
{ type: NodeTypes.TEXT, content: `hello` },
|
||||||
|
{ type: NodeTypes.ELEMENT, tag: 'p' }
|
||||||
|
]
|
||||||
|
])
|
||||||
|
const branch2 = (codegenNode.expressions[1] as ConditionalExpression)
|
||||||
|
.alternate as CallExpression
|
||||||
|
expect(branch2.arguments).toMatchObject([`_${EMPTY}`])
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('v-if + v-else', () => {
|
||||||
|
const {
|
||||||
|
root,
|
||||||
|
node: { codegenNode }
|
||||||
|
} = parseWithIfTransform(`<div v-if="ok"/><p v-else/>`)
|
||||||
|
assertSharedCodegen(codegenNode)
|
||||||
|
const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
|
||||||
|
.consequent as CallExpression
|
||||||
|
expect(branch1.arguments).toMatchObject([`"div"`, `{ key: 0 }`])
|
||||||
|
const branch2 = (codegenNode.expressions[1] as ConditionalExpression)
|
||||||
|
.alternate as CallExpression
|
||||||
|
expect(branch2.arguments).toMatchObject([`"p"`, `{ key: 1 }`])
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('v-if + v-else-if', () => {
|
||||||
|
const {
|
||||||
|
root,
|
||||||
|
node: { codegenNode }
|
||||||
|
} = parseWithIfTransform(`<div v-if="ok"/><p v-else-if="orNot" />`)
|
||||||
|
assertSharedCodegen(codegenNode, 1)
|
||||||
|
const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
|
||||||
|
.consequent as CallExpression
|
||||||
|
expect(branch1.arguments).toMatchObject([`"div"`, `{ key: 0 }`])
|
||||||
|
const branch2 = (codegenNode.expressions[1] as ConditionalExpression)
|
||||||
|
.alternate as ConditionalExpression
|
||||||
|
expect((branch2.consequent as CallExpression).arguments).toMatchObject([
|
||||||
|
`"p"`,
|
||||||
|
`{ key: 1 }`
|
||||||
|
])
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('v-if + v-else-if + v-else', () => {
|
||||||
|
const {
|
||||||
|
root,
|
||||||
|
node: { codegenNode }
|
||||||
|
} = parseWithIfTransform(
|
||||||
|
`<div v-if="ok"/><p v-else-if="orNot"/><template v-else>fine</template>`
|
||||||
|
)
|
||||||
|
assertSharedCodegen(codegenNode, 1)
|
||||||
|
const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
|
||||||
|
.consequent as CallExpression
|
||||||
|
expect(branch1.arguments).toMatchObject([`"div"`, `{ key: 0 }`])
|
||||||
|
const branch2 = (codegenNode.expressions[1] as ConditionalExpression)
|
||||||
|
.alternate as ConditionalExpression
|
||||||
|
expect((branch2.consequent as CallExpression).arguments).toMatchObject([
|
||||||
|
`"p"`,
|
||||||
|
`{ key: 1 }`
|
||||||
|
])
|
||||||
|
expect((branch2.alternate as CallExpression).arguments).toMatchObject([
|
||||||
|
`_${FRAGMENT}`,
|
||||||
|
`{ key: 2 }`,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: NodeTypes.TEXT,
|
||||||
|
content: `fine`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
])
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('key injection (only v-bind)', () => {
|
||||||
|
const {
|
||||||
|
node: { codegenNode }
|
||||||
|
} = parseWithIfTransform(`<div v-if="ok" v-bind="obj"/>`)
|
||||||
|
const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
|
||||||
|
.consequent as CallExpression
|
||||||
|
expect(branch1.arguments[1]).toMatchObject({
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
callee: `_${MERGE_PROPS}`,
|
||||||
|
arguments: [`{ key: 0 }`, { content: `obj` }]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('key injection (before v-bind)', () => {
|
||||||
|
const {
|
||||||
|
node: { codegenNode }
|
||||||
|
} = parseWithIfTransform(`<div v-if="ok" id="foo" v-bind="obj"/>`)
|
||||||
|
const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
|
||||||
|
.consequent as CallExpression
|
||||||
|
expect(branch1.arguments[1]).toMatchObject({
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
callee: `_${MERGE_PROPS}`,
|
||||||
|
arguments: [
|
||||||
|
createObjectMatcher({
|
||||||
|
key: '[0]',
|
||||||
|
id: 'foo'
|
||||||
|
}),
|
||||||
|
{ content: `obj` }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('key injection (after v-bind)', () => {
|
||||||
|
const {
|
||||||
|
node: { codegenNode }
|
||||||
|
} = parseWithIfTransform(`<div v-if="ok" v-bind="obj" id="foo"/>`)
|
||||||
|
const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
|
||||||
|
.consequent as CallExpression
|
||||||
|
expect(branch1.arguments[1]).toMatchObject({
|
||||||
|
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||||
|
callee: `_${MERGE_PROPS}`,
|
||||||
|
arguments: [
|
||||||
|
`{ key: 0 }`,
|
||||||
|
{ content: `obj` },
|
||||||
|
createObjectMatcher({
|
||||||
|
id: 'foo'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('key injection (w/ custom directive)', () => {
|
||||||
|
const {
|
||||||
|
node: { codegenNode }
|
||||||
|
} = parseWithIfTransform(`<div v-if="ok" v-foo />`)
|
||||||
|
const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
|
||||||
|
.consequent as CallExpression
|
||||||
|
expect(branch1.callee).toBe(`_${APPLY_DIRECTIVES}`)
|
||||||
|
const realBranch = branch1.arguments[0] as CallExpression
|
||||||
|
expect(realBranch.arguments[1]).toBe(`{ key: 0 }`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('with comments', () => {})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -300,7 +300,7 @@ function genNodeListAsArray(
|
|||||||
context: CodegenContext
|
context: CodegenContext
|
||||||
) {
|
) {
|
||||||
const multilines =
|
const multilines =
|
||||||
nodes.length > 1 ||
|
nodes.length > 3 ||
|
||||||
((!__BROWSER__ || __DEV__) &&
|
((!__BROWSER__ || __DEV__) &&
|
||||||
nodes.some(
|
nodes.some(
|
||||||
n =>
|
n =>
|
||||||
@ -513,7 +513,7 @@ function genFor(node: ForNode, context: CodegenContext) {
|
|||||||
function genCallExpression(
|
function genCallExpression(
|
||||||
node: CallExpression,
|
node: CallExpression,
|
||||||
context: CodegenContext,
|
context: CodegenContext,
|
||||||
multilines = node.arguments.length > 2
|
multilines = false
|
||||||
) {
|
) {
|
||||||
context.push(node.callee + `(`, node, true)
|
context.push(node.callee + `(`, node, true)
|
||||||
multilines && context.indent()
|
multilines && context.indent()
|
||||||
|
@ -19,7 +19,8 @@ import {
|
|||||||
JSChildNode,
|
JSChildNode,
|
||||||
ObjectExpression,
|
ObjectExpression,
|
||||||
createObjectProperty,
|
createObjectProperty,
|
||||||
Property
|
Property,
|
||||||
|
ExpressionNode
|
||||||
} from '../ast'
|
} from '../ast'
|
||||||
import { createCompilerError, ErrorCodes } from '../errors'
|
import { createCompilerError, ErrorCodes } from '../errors'
|
||||||
import { processExpression } from './transformExpression'
|
import { processExpression } from './transformExpression'
|
||||||
@ -28,7 +29,8 @@ import {
|
|||||||
CREATE_BLOCK,
|
CREATE_BLOCK,
|
||||||
EMPTY,
|
EMPTY,
|
||||||
FRAGMENT,
|
FRAGMENT,
|
||||||
APPLY_DIRECTIVES
|
APPLY_DIRECTIVES,
|
||||||
|
MERGE_PROPS
|
||||||
} from '../runtimeConstants'
|
} from '../runtimeConstants'
|
||||||
import { isString } from '@vue/shared'
|
import { isString } from '@vue/shared'
|
||||||
|
|
||||||
@ -51,14 +53,14 @@ export const transformIf = createStructuralDirectiveTransform(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dir.name === 'if') {
|
if (dir.name === 'if') {
|
||||||
|
const branch = createIfBranch(node, dir)
|
||||||
const codegenNode = createSequenceExpression([
|
const codegenNode = createSequenceExpression([
|
||||||
createCallExpression(context.helper(OPEN_BLOCK))
|
createCallExpression(context.helper(OPEN_BLOCK))
|
||||||
])
|
])
|
||||||
|
|
||||||
context.replaceNode({
|
context.replaceNode({
|
||||||
type: NodeTypes.IF,
|
type: NodeTypes.IF,
|
||||||
loc: node.loc,
|
loc: node.loc,
|
||||||
branches: [createIfBranch(node, dir)],
|
branches: [branch],
|
||||||
codegenNode
|
codegenNode
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -66,7 +68,7 @@ export const transformIf = createStructuralDirectiveTransform(
|
|||||||
// transformed.
|
// transformed.
|
||||||
return () => {
|
return () => {
|
||||||
codegenNode.expressions.push(
|
codegenNode.expressions.push(
|
||||||
createCodegenNodeForBranch(node, dir, 0, context)
|
createCodegenNodeForBranch(node, branch, 0, context)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -104,7 +106,7 @@ export const transformIf = createStructuralDirectiveTransform(
|
|||||||
} else {
|
} else {
|
||||||
parentCondition.alternate = createCodegenNodeForBranch(
|
parentCondition.alternate = createCodegenNodeForBranch(
|
||||||
node,
|
node,
|
||||||
dir,
|
branch,
|
||||||
sibling.branches.length - 1,
|
sibling.branches.length - 1,
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
@ -138,25 +140,26 @@ function createIfBranch(node: ElementNode, dir: DirectiveNode): IfBranchNode {
|
|||||||
|
|
||||||
function createCodegenNodeForBranch(
|
function createCodegenNodeForBranch(
|
||||||
node: ElementNode,
|
node: ElementNode,
|
||||||
dir: DirectiveNode,
|
branch: IfBranchNode,
|
||||||
index: number,
|
index: number,
|
||||||
context: TransformContext
|
context: TransformContext
|
||||||
): ConditionalExpression | CallExpression {
|
): ConditionalExpression | CallExpression {
|
||||||
if (dir.exp) {
|
if (branch.condition) {
|
||||||
return createConditionalExpression(
|
return createConditionalExpression(
|
||||||
dir.exp,
|
branch.condition,
|
||||||
createChildrenCodegenNode(node, index, context),
|
createChildrenCodegenNode(node, branch, index, context),
|
||||||
createCallExpression(context.helper(CREATE_BLOCK), [
|
createCallExpression(context.helper(CREATE_BLOCK), [
|
||||||
context.helper(EMPTY)
|
context.helper(EMPTY)
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return createChildrenCodegenNode(node, index, context)
|
return createChildrenCodegenNode(node, branch, index, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createChildrenCodegenNode(
|
function createChildrenCodegenNode(
|
||||||
node: ElementNode,
|
node: ElementNode,
|
||||||
|
branch: IfBranchNode,
|
||||||
index: number,
|
index: number,
|
||||||
{ helper }: TransformContext
|
{ helper }: TransformContext
|
||||||
): CallExpression {
|
): CallExpression {
|
||||||
@ -166,11 +169,11 @@ function createChildrenCodegenNode(
|
|||||||
return createCallExpression(helper(CREATE_BLOCK), [
|
return createCallExpression(helper(CREATE_BLOCK), [
|
||||||
helper(FRAGMENT),
|
helper(FRAGMENT),
|
||||||
keyExp,
|
keyExp,
|
||||||
node.children
|
branch.children
|
||||||
])
|
])
|
||||||
} else {
|
} else {
|
||||||
let childCodegen = node.codegenNode!
|
let childCodegen = node.codegenNode!
|
||||||
if (childCodegen.callee === helper(APPLY_DIRECTIVES)) {
|
if (childCodegen.callee.includes(APPLY_DIRECTIVES)) {
|
||||||
childCodegen = childCodegen.arguments[0] as CallExpression
|
childCodegen = childCodegen.arguments[0] as CallExpression
|
||||||
}
|
}
|
||||||
// change child to a block
|
// change child to a block
|
||||||
@ -181,7 +184,10 @@ function createChildrenCodegenNode(
|
|||||||
childCodegen.arguments[1] = keyExp
|
childCodegen.arguments[1] = keyExp
|
||||||
} else {
|
} else {
|
||||||
// inject branch key if not already have a key
|
// inject branch key if not already have a key
|
||||||
const props = existingProps as CallExpression | ObjectExpression
|
const props = existingProps as
|
||||||
|
| CallExpression
|
||||||
|
| ObjectExpression
|
||||||
|
| ExpressionNode
|
||||||
if (props.type === NodeTypes.JS_CALL_EXPRESSION) {
|
if (props.type === NodeTypes.JS_CALL_EXPRESSION) {
|
||||||
// merged props... add ours
|
// merged props... add ours
|
||||||
// only inject key to object literal if it's the first argument so that
|
// only inject key to object literal if it's the first argument so that
|
||||||
@ -192,11 +198,17 @@ function createChildrenCodegenNode(
|
|||||||
} else {
|
} else {
|
||||||
props.arguments.unshift(keyExp)
|
props.arguments.unshift(keyExp)
|
||||||
}
|
}
|
||||||
} else {
|
} else if (props.type === NodeTypes.JS_OBJECT_EXPRESSION) {
|
||||||
props.properties.unshift(createKeyProperty(index))
|
props.properties.unshift(createKeyProperty(index))
|
||||||
|
} else {
|
||||||
|
// single v-bind with expression
|
||||||
|
childCodegen.arguments[1] = createCallExpression(helper(MERGE_PROPS), [
|
||||||
|
keyExp,
|
||||||
|
props
|
||||||
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return childCodegen
|
return node.codegenNode!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user