Merge remote-tracking branch 'github/master' into changing_unwrap_ref

# Conflicts:
#	packages/reactivity/src/ref.ts
#	packages/runtime-core/__tests__/apiTemplateRef.spec.ts
#	packages/runtime-core/src/apiWatch.ts
This commit is contained in:
pikax
2020-04-08 21:21:04 +01:00
339 changed files with 26645 additions and 8965 deletions

View File

@@ -2,8 +2,8 @@
exports[`compiler: codegen ArrayExpression 1`] = `
"
return function render() {
with (this) {
return function render(_ctx, _cache) {
with (_ctx) {
return [
foo,
bar(baz)
@@ -14,22 +14,18 @@ return function render() {
exports[`compiler: codegen CacheExpression 1`] = `
"
export default function render() {
const _ctx = this
const _cache = _ctx.$cache
export function render(_ctx, _cache) {
return _cache[1] || (_cache[1] = foo)
}"
`;
exports[`compiler: codegen CacheExpression w/ isVNode: true 1`] = `
"
export default function render() {
const _ctx = this
const _cache = _ctx.$cache
export function render(_ctx, _cache) {
return _cache[1] || (
setBlockTracking(-1),
_setBlockTracking(-1),
_cache[1] = foo,
setBlockTracking(1),
_setBlockTracking(1),
_cache[1]
)
}"
@@ -37,8 +33,8 @@ export default function render() {
exports[`compiler: codegen ConditionalExpression 1`] = `
"
return function render() {
with (this) {
return function render(_ctx, _cache) {
with (_ctx) {
return ok
? foo()
: orNot
@@ -50,8 +46,8 @@ return function render() {
exports[`compiler: codegen Element (callExpression + objectExpression + TemplateChildNode[]) 1`] = `
"
return function render() {
with (this) {
return function render(_ctx, _cache) {
with (_ctx) {
return _createVNode(\\"div\\", {
id: \\"foo\\",
[prop]: bar,
@@ -63,24 +59,17 @@ return function render() {
}"
`;
exports[`compiler: codegen SequenceExpression 1`] = `
exports[`compiler: codegen assets + temps 1`] = `
"
return function render() {
with (this) {
return (foo, bar(baz))
}
}"
`;
exports[`compiler: codegen assets 1`] = `
"
return function render() {
with (this) {
return function render(_ctx, _cache) {
with (_ctx) {
const _component_Foo = _resolveComponent(\\"Foo\\")
const _component_bar_baz = _resolveComponent(\\"bar-baz\\")
const _component_barbaz = _resolveComponent(\\"barbaz\\")
const _directive_my_dir = _resolveDirective(\\"my_dir\\")
const _directive_my_dir_0 = _resolveDirective(\\"my_dir_0\\")
const _directive_my_dir_1 = _resolveDirective(\\"my_dir_1\\")
let _temp0, _temp1, _temp2
return null
}
}"
@@ -88,8 +77,8 @@ return function render() {
exports[`compiler: codegen comment 1`] = `
"
return function render() {
with (this) {
return function render(_ctx, _cache) {
with (_ctx) {
return _createCommentVNode(\\"foo\\")
}
}"
@@ -97,18 +86,18 @@ return function render() {
exports[`compiler: codegen compound expression 1`] = `
"
return function render() {
with (this) {
return _ctx.foo + _toString(bar)
return function render(_ctx, _cache) {
with (_ctx) {
return _ctx.foo + _toDisplayString(bar) + nested
}
}"
`;
exports[`compiler: codegen forNode 1`] = `
"
return function render() {
with (this) {
return (foo, bar)
return function render(_ctx, _cache) {
with (_ctx) {
return (_openBlock(true), _createBlock(_Fragment, null, _renderList(), 1))
}
}"
`;
@@ -116,20 +105,19 @@ return function render() {
exports[`compiler: codegen function mode preamble 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, resolveDirective: _resolveDirective } = _Vue
return null
}
}"
`;
exports[`compiler: codegen function mode preamble w/ prefixIdentifiers: true 1`] = `
"const { createVNode, resolveDirective } = Vue
"const { createVNode: _createVNode, resolveDirective: _resolveDirective } = Vue
return function render() {
const _ctx = this
return function render(_ctx, _cache) {
return null
}"
`;
@@ -139,8 +127,8 @@ exports[`compiler: codegen hoists 1`] = `
const _hoisted_1 = hello
const _hoisted_2 = { id: \\"foo\\" }
return function render() {
with (this) {
return function render(_ctx, _cache) {
with (_ctx) {
return null
}
}"
@@ -148,44 +136,59 @@ return function render() {
exports[`compiler: codegen ifNode 1`] = `
"
return function render() {
with (this) {
return (foo, bar)
return function render(_ctx, _cache) {
with (_ctx) {
return foo
? bar
: baz
}
}"
`;
exports[`compiler: codegen interpolation 1`] = `
"
return function render() {
with (this) {
return _toString(hello)
return function render(_ctx, _cache) {
with (_ctx) {
return _toDisplayString(hello)
}
}"
`;
exports[`compiler: codegen module mode preamble 1`] = `
"import { createVNode, resolveDirective } from \\"vue\\"
"import { createVNode as _createVNode, resolveDirective as _resolveDirective } from \\"vue\\"
export default function render() {
const _ctx = this
export function render(_ctx, _cache) {
return null
}"
`;
exports[`compiler: codegen prefixIdentifiers: true should inject _ctx statement 1`] = `
"
return function render() {
const _ctx = this
exports[`compiler: codegen module mode preamble w/ optimizeBindings: true 1`] = `
"import { createVNode, resolveDirective } from \\"vue\\"
// Binding optimization for webpack code-split
const _createVNode = createVNode, _resolveDirective = resolveDirective
export function render(_ctx, _cache) {
return null
}"
`;
exports[`compiler: codegen static text 1`] = `
"
return function render() {
with (this) {
return function render(_ctx, _cache) {
with (_ctx) {
return \\"hello\\"
}
}"
`;
exports[`compiler: codegen temps 1`] = `
"
return function render(_ctx, _cache) {
with (_ctx) {
let _temp0, _temp1, _temp2
return null
}
}"
`;

View File

@@ -3,21 +3,23 @@
exports[`compiler: integration tests function mode 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { toString: _toString, openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, createCommentVNode: _createCommentVNode, Fragment: _Fragment, renderList: _renderList, createTextVNode: _createTextVNode } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode, createTextVNode: _createTextVNode, Fragment: _Fragment, renderList: _renderList } = _Vue
return (_openBlock(), _createBlock(\\"div\\", {
id: \\"foo\\",
class: bar.baz
}, [
_createTextVNode(_toString(world.burn()) + \\" \\", 1 /* TEXT */),
(_openBlock(), ok
? _createBlock(\\"div\\", { key: 0 }, \\"yes\\")
: _createBlock(_Fragment, { key: 1 }, [\\"no\\"])),
(_openBlock(false), _createBlock(_Fragment, null, _renderList(list, (value, index) => {
_createTextVNode(_toDisplayString(world.burn()) + \\" \\", 1 /* TEXT */),
ok
? (_openBlock(), _createBlock(\\"div\\", { key: 0 }, \\"yes\\"))
: (_openBlock(), _createBlock(_Fragment, { key: 1 }, [
_createTextVNode(\\"no\\")
], 64 /* STABLE_FRAGMENT */)),
(_openBlock(true), _createBlock(_Fragment, null, _renderList(list, (value, index) => {
return (_openBlock(), _createBlock(\\"div\\", null, [
_createVNode(\\"span\\", null, _toString(value + index), 1 /* TEXT */)
_createVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */)
]))
}), 256 /* UNKEYED_FRAGMENT */))
], 2 /* CLASS */))
@@ -26,21 +28,22 @@ return function render() {
`;
exports[`compiler: integration tests function mode w/ prefixIdentifiers: true 1`] = `
"const { toString, openBlock, createVNode, createBlock, createCommentVNode, Fragment, renderList, createTextVNode } = Vue
"const { toDisplayString: _toDisplayString, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode, createTextVNode: _createTextVNode, Fragment: _Fragment, renderList: _renderList } = Vue
return function render() {
const _ctx = this
return (openBlock(), createBlock(\\"div\\", {
return function render(_ctx, _cache) {
return (_openBlock(), _createBlock(\\"div\\", {
id: \\"foo\\",
class: _ctx.bar.baz
}, [
createTextVNode(toString(_ctx.world.burn()) + \\" \\", 1 /* TEXT */),
(openBlock(), (_ctx.ok)
? createBlock(\\"div\\", { key: 0 }, \\"yes\\")
: createBlock(Fragment, { key: 1 }, [\\"no\\"])),
(openBlock(false), createBlock(Fragment, null, renderList(_ctx.list, (value, index) => {
return (openBlock(), createBlock(\\"div\\", null, [
createVNode(\\"span\\", null, toString(value + index), 1 /* TEXT */)
_createTextVNode(_toDisplayString(_ctx.world.burn()) + \\" \\", 1 /* TEXT */),
(_ctx.ok)
? (_openBlock(), _createBlock(\\"div\\", { key: 0 }, \\"yes\\"))
: (_openBlock(), _createBlock(_Fragment, { key: 1 }, [
_createTextVNode(\\"no\\")
], 64 /* STABLE_FRAGMENT */)),
(_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.list, (value, index) => {
return (_openBlock(), _createBlock(\\"div\\", null, [
_createVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */)
]))
}), 256 /* UNKEYED_FRAGMENT */))
], 2 /* CLASS */))
@@ -48,21 +51,22 @@ return function render() {
`;
exports[`compiler: integration tests module mode 1`] = `
"import { toString, openBlock, createVNode, createBlock, createCommentVNode, Fragment, renderList, createTextVNode } from \\"vue\\"
"import { toDisplayString as _toDisplayString, createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock, createCommentVNode as _createCommentVNode, createTextVNode as _createTextVNode, Fragment as _Fragment, renderList as _renderList } from \\"vue\\"
export default function render() {
const _ctx = this
return (openBlock(), createBlock(\\"div\\", {
export function render(_ctx, _cache) {
return (_openBlock(), _createBlock(\\"div\\", {
id: \\"foo\\",
class: _ctx.bar.baz
}, [
createTextVNode(toString(_ctx.world.burn()) + \\" \\", 1 /* TEXT */),
(openBlock(), (_ctx.ok)
? createBlock(\\"div\\", { key: 0 }, \\"yes\\")
: createBlock(Fragment, { key: 1 }, [\\"no\\"])),
(openBlock(false), createBlock(Fragment, null, renderList(_ctx.list, (value, index) => {
return (openBlock(), createBlock(\\"div\\", null, [
createVNode(\\"span\\", null, toString(value + index), 1 /* TEXT */)
_createTextVNode(_toDisplayString(_ctx.world.burn()) + \\" \\", 1 /* TEXT */),
(_ctx.ok)
? (_openBlock(), _createBlock(\\"div\\", { key: 0 }, \\"yes\\"))
: (_openBlock(), _createBlock(_Fragment, { key: 1 }, [
_createTextVNode(\\"no\\")
], 64 /* STABLE_FRAGMENT */)),
(_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.list, (value, index) => {
return (_openBlock(), _createBlock(\\"div\\", null, [
_createVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */)
]))
}), 256 /* UNKEYED_FRAGMENT */))
], 2 /* CLASS */))

View File

@@ -0,0 +1,91 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`scopeId compiler support should push scopeId for hoisted nodes 1`] = `
"import { createVNode as _createVNode, toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from \\"vue\\"
const _withId = _withScopeId(\\"test\\")
_pushScopeId(\\"test\\")
const _hoisted_1 = _createVNode(\\"div\\", null, \\"hello\\", -1 /* HOISTED */)
const _hoisted_2 = _createVNode(\\"div\\", null, \\"world\\", -1 /* HOISTED */)
_popScopeId()
export const render = _withId(function render(_ctx, _cache) {
return (_openBlock(), _createBlock(\\"div\\", null, [
_hoisted_1,
_createTextVNode(_toDisplayString(_ctx.foo), 1 /* TEXT */),
_hoisted_2
]))
})"
`;
exports[`scopeId compiler support should wrap default slot 1`] = `
"import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
const _withId = _withScopeId(\\"test\\")
export const render = _withId(function render(_ctx, _cache) {
const _component_Child = _resolveComponent(\\"Child\\")
return (_openBlock(), _createBlock(_component_Child, null, {
default: _withId(() => [
_createVNode(\\"div\\")
]),
_: 1
}))
})"
`;
exports[`scopeId compiler support should wrap dynamic slots 1`] = `
"import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, renderList as _renderList, createSlots as _createSlots, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
const _withId = _withScopeId(\\"test\\")
export const render = _withId(function render(_ctx, _cache) {
const _component_Child = _resolveComponent(\\"Child\\")
return (_openBlock(), _createBlock(_component_Child, null, _createSlots({ _: 1 }, [
(_ctx.ok)
? {
name: \\"foo\\",
fn: _withId(() => [
_createVNode(\\"div\\")
])
}
: undefined,
_renderList(_ctx.list, (i) => {
return {
name: i,
fn: _withId(() => [
_createVNode(\\"div\\")
])
}
})
]), 1024 /* DYNAMIC_SLOTS */))
})"
`;
exports[`scopeId compiler support should wrap named slots 1`] = `
"import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
const _withId = _withScopeId(\\"test\\")
export const render = _withId(function render(_ctx, _cache) {
const _component_Child = _resolveComponent(\\"Child\\")
return (_openBlock(), _createBlock(_component_Child, null, {
foo: _withId(({ msg }) => [
_createTextVNode(_toDisplayString(msg), 1 /* TEXT */)
]),
bar: _withId(() => [
_createVNode(\\"div\\")
]),
_: 1
}))
})"
`;
exports[`scopeId compiler support should wrap render function 1`] = `
"import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
const _withId = _withScopeId(\\"test\\")
export const render = _withId(function render(_ctx, _cache) {
return (_openBlock(), _createBlock(\\"div\\"))
})"
`;

View File

@@ -9,20 +9,28 @@ import {
createArrayExpression,
createCompoundExpression,
createInterpolation,
createSequenceExpression,
createCallExpression,
createConditionalExpression,
IfCodegenNode,
ForCodegenNode,
createCacheExpression
createCacheExpression,
createTemplateLiteral,
createBlockStatement,
createIfStatement,
createAssignmentExpression,
IfConditionalExpression,
createVNodeCall,
VNodeCall,
DirectiveArguments
} from '../src'
import {
CREATE_VNODE,
TO_STRING,
TO_DISPLAY_STRING,
RESOLVE_DIRECTIVE,
helperNameMap,
RESOLVE_COMPONENT,
CREATE_COMMENT
CREATE_COMMENT,
FRAGMENT,
RENDER_LIST
} from '../src/runtimeHelpers'
import { createElementWithCodegen } from './testUtils'
import { PatchFlags } from '@vue/shared'
@@ -37,6 +45,7 @@ function createRoot(options: Partial<RootNode> = {}): RootNode {
imports: [],
hoists: [],
cached: 0,
temps: 0,
codegenNode: createSimpleExpression(`null`, false),
loc: locStub,
...options
@@ -49,11 +58,33 @@ describe('compiler: codegen', () => {
helpers: [CREATE_VNODE, RESOLVE_DIRECTIVE]
})
const { code } = generate(root, { mode: 'module' })
expect(code).toMatch(
`import { ${helperNameMap[CREATE_VNODE]} as _${
helperNameMap[CREATE_VNODE]
}, ${helperNameMap[RESOLVE_DIRECTIVE]} as _${
helperNameMap[RESOLVE_DIRECTIVE]
} } from "vue"`
)
expect(code).toMatchSnapshot()
})
test('module mode preamble w/ optimizeBindings: true', () => {
const root = createRoot({
helpers: [CREATE_VNODE, RESOLVE_DIRECTIVE]
})
const { code } = generate(root, { mode: 'module', optimizeBindings: true })
expect(code).toMatch(
`import { ${helperNameMap[CREATE_VNODE]}, ${
helperNameMap[RESOLVE_DIRECTIVE]
} } from "vue"`
)
expect(code).toMatch(
`const _${helperNameMap[CREATE_VNODE]} = ${
helperNameMap[CREATE_VNODE]
}, _${helperNameMap[RESOLVE_DIRECTIVE]} = ${
helperNameMap[RESOLVE_DIRECTIVE]
}`
)
expect(code).toMatchSnapshot()
})
@@ -83,17 +114,20 @@ describe('compiler: codegen', () => {
})
expect(code).not.toMatch(`const _Vue = Vue`)
expect(code).toMatch(
`const { ${helperNameMap[CREATE_VNODE]}, ${
`const { ${helperNameMap[CREATE_VNODE]}: _${
helperNameMap[CREATE_VNODE]
}, ${helperNameMap[RESOLVE_DIRECTIVE]}: _${
helperNameMap[RESOLVE_DIRECTIVE]
} } = Vue`
)
expect(code).toMatchSnapshot()
})
test('assets', () => {
test('assets + temps', () => {
const root = createRoot({
components: [`Foo`, `bar-baz`, `barbaz`],
directives: [`my_dir`]
directives: [`my_dir_0`, `my_dir_1`],
temps: 3
})
const { code } = generate(root, { mode: 'function' })
expect(code).toMatch(
@@ -110,10 +144,16 @@ describe('compiler: codegen', () => {
}("barbaz")\n`
)
expect(code).toMatch(
`const _directive_my_dir = _${
`const _directive_my_dir_0 = _${
helperNameMap[RESOLVE_DIRECTIVE]
}("my_dir")\n`
}("my_dir_0")\n`
)
expect(code).toMatch(
`const _directive_my_dir_1 = _${
helperNameMap[RESOLVE_DIRECTIVE]
}("my_dir_1")\n`
)
expect(code).toMatch(`let _temp0, _temp1, _temp2`)
expect(code).toMatchSnapshot()
})
@@ -138,9 +178,12 @@ describe('compiler: codegen', () => {
expect(code).toMatchSnapshot()
})
test('prefixIdentifiers: true should inject _ctx statement', () => {
const { code } = generate(createRoot(), { prefixIdentifiers: true })
expect(code).toMatch(`const _ctx = this\n`)
test('temps', () => {
const root = createRoot({
temps: 3
})
const { code } = generate(root)
expect(code).toMatch(`let _temp0, _temp1, _temp2`)
expect(code).toMatchSnapshot()
})
@@ -164,7 +207,7 @@ describe('compiler: codegen', () => {
codegenNode: createInterpolation(`hello`, locStub)
})
)
expect(code).toMatch(`return _${helperNameMap[TO_STRING]}(hello)`)
expect(code).toMatch(`return _${helperNameMap[TO_DISPLAY_STRING]}(hello)`)
expect(code).toMatchSnapshot()
})
@@ -193,11 +236,15 @@ describe('compiler: codegen', () => {
type: NodeTypes.INTERPOLATION,
loc: locStub,
content: createSimpleExpression(`bar`, false, locStub)
}
},
// nested compound
createCompoundExpression([` + `, `nested`])
])
})
)
expect(code).toMatch(`return _ctx.foo + _${helperNameMap[TO_STRING]}(bar)`)
expect(code).toMatch(
`return _ctx.foo + _${helperNameMap[TO_DISPLAY_STRING]}(bar) + nested`
)
expect(code).toMatchSnapshot()
})
@@ -208,14 +255,15 @@ describe('compiler: codegen', () => {
type: NodeTypes.IF,
loc: locStub,
branches: [],
codegenNode: createSequenceExpression([
codegenNode: createConditionalExpression(
createSimpleExpression('foo', false),
createSimpleExpression('bar', false)
]) as IfCodegenNode
createSimpleExpression('bar', false),
createSimpleExpression('baz', false)
) as IfConditionalExpression
}
})
)
expect(code).toMatch(`return (foo, bar)`)
expect(code).toMatch(/return foo\s+\? bar\s+: baz/)
expect(code).toMatchSnapshot()
})
@@ -230,21 +278,30 @@ describe('compiler: codegen', () => {
keyAlias: undefined,
objectIndexAlias: undefined,
children: [],
codegenNode: createSequenceExpression([
createSimpleExpression('foo', false),
createSimpleExpression('bar', false)
]) as ForCodegenNode
parseResult: {} as any,
codegenNode: {
type: NodeTypes.VNODE_CALL,
tag: FRAGMENT,
isBlock: true,
isForBlock: true,
props: undefined,
children: createCallExpression(RENDER_LIST),
patchFlag: '1',
dynamicProps: undefined,
directives: undefined,
loc: locStub
} as ForCodegenNode
}
})
)
expect(code).toMatch(`return (foo, bar)`)
expect(code).toMatch(`openBlock(true)`)
expect(code).toMatchSnapshot()
})
test('Element (callExpression + objectExpression + TemplateChildNode[])', () => {
const { code } = generate(
createRoot({
codegenNode: createElementWithCodegen([
codegenNode: createElementWithCodegen(
// string
`"div"`,
// ObjectExpression
@@ -275,7 +332,7 @@ describe('compiler: codegen', () => {
),
// ChildNode[]
[
createElementWithCodegen([
createElementWithCodegen(
`"p"`,
createObjectExpression(
[
@@ -287,11 +344,11 @@ describe('compiler: codegen', () => {
],
locStub
)
])
)
],
// flag
PatchFlags.FULL_PROPS + ''
])
)
})
)
expect(code).toMatch(`
@@ -321,19 +378,6 @@ describe('compiler: codegen', () => {
expect(code).toMatchSnapshot()
})
test('SequenceExpression', () => {
const { code } = generate(
createRoot({
codegenNode: createSequenceExpression([
createSimpleExpression(`foo`, false),
createCallExpression(`bar`, [`baz`])
])
})
)
expect(code).toMatch(`return (foo, bar(baz))`)
expect(code).toMatchSnapshot()
})
test('ConditionalExpression', () => {
const { code } = generate(
createRoot({
@@ -372,7 +416,6 @@ describe('compiler: codegen', () => {
prefixIdentifiers: true
}
)
expect(code).toMatch(`const _cache = _ctx.$cache`)
expect(code).toMatch(`_cache[1] || (_cache[1] = foo)`)
expect(code).toMatchSnapshot()
})
@@ -392,17 +435,309 @@ describe('compiler: codegen', () => {
prefixIdentifiers: true
}
)
expect(code).toMatch(`const _cache = _ctx.$cache`)
expect(code).toMatch(
`
_cache[1] || (
setBlockTracking(-1),
_setBlockTracking(-1),
_cache[1] = foo,
setBlockTracking(1),
_setBlockTracking(1),
_cache[1]
)
`.trim()
)
expect(code).toMatchSnapshot()
})
test('TemplateLiteral', () => {
const { code } = generate(
createRoot({
codegenNode: createCallExpression(`_push`, [
createTemplateLiteral([
`foo`,
createCallExpression(`_renderAttr`, ['id', 'foo']),
`bar`
])
])
}),
{ ssr: true, mode: 'module' }
)
expect(code).toMatchInlineSnapshot(`
"
export function ssrRender(_ctx, _push, _parent) {
_push(\`foo\${_renderAttr(id, foo)}bar\`)
}"
`)
})
describe('IfStatement', () => {
test('if', () => {
const { code } = generate(
createRoot({
codegenNode: createBlockStatement([
createIfStatement(
createSimpleExpression('foo', false),
createBlockStatement([createCallExpression(`ok`)])
)
])
}),
{ ssr: true, mode: 'module' }
)
expect(code).toMatchInlineSnapshot(`
"
export function ssrRender(_ctx, _push, _parent) {
if (foo) {
ok()
}
}"
`)
})
test('if/else', () => {
const { code } = generate(
createRoot({
codegenNode: createBlockStatement([
createIfStatement(
createSimpleExpression('foo', false),
createBlockStatement([createCallExpression(`foo`)]),
createBlockStatement([createCallExpression('bar')])
)
])
}),
{ ssr: true, mode: 'module' }
)
expect(code).toMatchInlineSnapshot(`
"
export function ssrRender(_ctx, _push, _parent) {
if (foo) {
foo()
} else {
bar()
}
}"
`)
})
test('if/else-if', () => {
const { code } = generate(
createRoot({
codegenNode: createBlockStatement([
createIfStatement(
createSimpleExpression('foo', false),
createBlockStatement([createCallExpression(`foo`)]),
createIfStatement(
createSimpleExpression('bar', false),
createBlockStatement([createCallExpression(`bar`)])
)
)
])
}),
{ ssr: true, mode: 'module' }
)
expect(code).toMatchInlineSnapshot(`
"
export function ssrRender(_ctx, _push, _parent) {
if (foo) {
foo()
} else if (bar) {
bar()
}
}"
`)
})
test('if/else-if/else', () => {
const { code } = generate(
createRoot({
codegenNode: createBlockStatement([
createIfStatement(
createSimpleExpression('foo', false),
createBlockStatement([createCallExpression(`foo`)]),
createIfStatement(
createSimpleExpression('bar', false),
createBlockStatement([createCallExpression(`bar`)]),
createBlockStatement([createCallExpression('baz')])
)
)
])
}),
{ ssr: true, mode: 'module' }
)
expect(code).toMatchInlineSnapshot(`
"
export function ssrRender(_ctx, _push, _parent) {
if (foo) {
foo()
} else if (bar) {
bar()
} else {
baz()
}
}"
`)
})
})
test('AssignmentExpression', () => {
const { code } = generate(
createRoot({
codegenNode: createAssignmentExpression(
createSimpleExpression(`foo`, false),
createSimpleExpression(`bar`, false)
)
})
)
expect(code).toMatchInlineSnapshot(`
"
return function render(_ctx, _cache) {
with (_ctx) {
return foo = bar
}
}"
`)
})
describe('VNodeCall', () => {
function genCode(node: VNodeCall) {
return generate(
createRoot({
codegenNode: node
})
).code.match(/with \(_ctx\) \{\s+([^]+)\s+\}\s+\}$/)![1]
}
const mockProps = createObjectExpression([
createObjectProperty(`foo`, createSimpleExpression(`bar`, true))
])
const mockChildren = createCompoundExpression(['children'])
const mockDirs = createArrayExpression([
createArrayExpression([`foo`, createSimpleExpression(`bar`, false)])
]) as DirectiveArguments
test('tag only', () => {
expect(genCode(createVNodeCall(null, `"div"`))).toMatchInlineSnapshot(`
"return _createVNode(\\"div\\")
"
`)
expect(genCode(createVNodeCall(null, FRAGMENT))).toMatchInlineSnapshot(`
"return _createVNode(_Fragment)
"
`)
})
test('with props', () => {
expect(genCode(createVNodeCall(null, `"div"`, mockProps)))
.toMatchInlineSnapshot(`
"return _createVNode(\\"div\\", { foo: \\"bar\\" })
"
`)
})
test('with children, no props', () => {
expect(genCode(createVNodeCall(null, `"div"`, undefined, mockChildren)))
.toMatchInlineSnapshot(`
"return _createVNode(\\"div\\", null, children)
"
`)
})
test('with children + props', () => {
expect(genCode(createVNodeCall(null, `"div"`, mockProps, mockChildren)))
.toMatchInlineSnapshot(`
"return _createVNode(\\"div\\", { foo: \\"bar\\" }, children)
"
`)
})
test('with patchFlag and no children/props', () => {
expect(genCode(createVNodeCall(null, `"div"`, undefined, undefined, '1')))
.toMatchInlineSnapshot(`
"return _createVNode(\\"div\\", null, null, 1)
"
`)
})
test('as block', () => {
expect(
genCode(
createVNodeCall(
null,
`"div"`,
mockProps,
mockChildren,
undefined,
undefined,
undefined,
true
)
)
).toMatchInlineSnapshot(`
"return (_openBlock(), _createBlock(\\"div\\", { foo: \\"bar\\" }, children))
"
`)
})
test('as for block', () => {
expect(
genCode(
createVNodeCall(
null,
`"div"`,
mockProps,
mockChildren,
undefined,
undefined,
undefined,
true,
true
)
)
).toMatchInlineSnapshot(`
"return (_openBlock(true), _createBlock(\\"div\\", { foo: \\"bar\\" }, children))
"
`)
})
test('with directives', () => {
expect(
genCode(
createVNodeCall(
null,
`"div"`,
mockProps,
mockChildren,
undefined,
undefined,
mockDirs
)
)
).toMatchInlineSnapshot(`
"return _withDirectives(_createVNode(\\"div\\", { foo: \\"bar\\" }, children), [
[foo, bar]
])
"
`)
})
test('block + directives', () => {
expect(
genCode(
createVNodeCall(
null,
`"div"`,
mockProps,
mockChildren,
undefined,
undefined,
mockDirs,
true
)
)
).toMatchInlineSnapshot(`
"return _withDirectives((_openBlock(), _createBlock(\\"div\\", { foo: \\"bar\\" }, children)), [
[foo, bar]
])
"
`)
})
})
})

View File

@@ -44,7 +44,7 @@ describe('compiler: integration tests', () => {
return res
}
test('function mode', async () => {
test('function mode', () => {
const { code, map } = compile(source, {
sourceMap: true,
filename: `foo.vue`
@@ -54,7 +54,7 @@ describe('compiler: integration tests', () => {
expect(map!.sources).toEqual([`foo.vue`])
expect(map!.sourcesContent).toEqual([source])
const consumer = await new SourceMapConsumer(map as RawSourceMap)
const consumer = new SourceMapConsumer(map as RawSourceMap)
expect(
consumer.originalPositionFor(getPositionInCode(code, `id`))
@@ -109,7 +109,7 @@ describe('compiler: integration tests', () => {
).toMatchObject(getPositionInCode(source, `value + index`))
})
test('function mode w/ prefixIdentifiers: true', async () => {
test('function mode w/ prefixIdentifiers: true', () => {
const { code, map } = compile(source, {
sourceMap: true,
filename: `foo.vue`,
@@ -120,7 +120,7 @@ describe('compiler: integration tests', () => {
expect(map!.sources).toEqual([`foo.vue`])
expect(map!.sourcesContent).toEqual([source])
const consumer = await new SourceMapConsumer(map as RawSourceMap)
const consumer = new SourceMapConsumer(map as RawSourceMap)
expect(
consumer.originalPositionFor(getPositionInCode(code, `id`))
@@ -184,7 +184,7 @@ describe('compiler: integration tests', () => {
).toMatchObject(getPositionInCode(source, `value + index`))
})
test('module mode', async () => {
test('module mode', () => {
const { code, map } = compile(source, {
mode: 'module',
sourceMap: true,
@@ -195,7 +195,7 @@ describe('compiler: integration tests', () => {
expect(map!.sources).toEqual([`foo.vue`])
expect(map!.sourcesContent).toEqual([source])
const consumer = await new SourceMapConsumer(map as RawSourceMap)
const consumer = new SourceMapConsumer(map as RawSourceMap)
expect(
consumer.originalPositionFor(getPositionInCode(code, `id`))

View File

@@ -1,5 +1,5 @@
import { ParserOptions } from '../src/options'
import { parse, TextModes } from '../src/parse'
import { baseParse, TextModes } from '../src/parse'
import { ErrorCodes } from '../src/errors'
import {
CommentNode,
@@ -16,7 +16,7 @@ import {
describe('compiler: parse', () => {
describe('Text', () => {
test('simple text', () => {
const ast = parse('some text')
const ast = baseParse('some text')
const text = ast.children[0] as TextNode
expect(text).toStrictEqual({
@@ -31,7 +31,7 @@ describe('compiler: parse', () => {
})
test('simple text with invalid end tag', () => {
const ast = parse('some text</div>', {
const ast = baseParse('some text</div>', {
onError: () => {}
})
const text = ast.children[0] as TextNode
@@ -48,7 +48,7 @@ describe('compiler: parse', () => {
})
test('text with interpolation', () => {
const ast = parse('some {{ foo + bar }} text')
const ast = baseParse('some {{ foo + bar }} text')
const text1 = ast.children[0] as TextNode
const text2 = ast.children[2] as TextNode
@@ -73,7 +73,7 @@ describe('compiler: parse', () => {
})
test('text with interpolation which has `<`', () => {
const ast = parse('some {{ a<b && c>d }} text')
const ast = baseParse('some {{ a<b && c>d }} text')
const text1 = ast.children[0] as TextNode
const text2 = ast.children[2] as TextNode
@@ -98,7 +98,7 @@ describe('compiler: parse', () => {
})
test('text with mix of tags and interpolations', () => {
const ast = parse('some <span>{{ foo < bar + foo }} text</span>')
const ast = baseParse('some <span>{{ foo < bar + foo }} text</span>')
const text1 = ast.children[0] as TextNode
const text2 = (ast.children[1] as ElementNode).children![1] as TextNode
@@ -123,7 +123,7 @@ describe('compiler: parse', () => {
})
test('lonly "<" don\'t separate nodes', () => {
const ast = parse('a < b', {
const ast = baseParse('a < b', {
onError: err => {
if (err.code !== ErrorCodes.INVALID_FIRST_CHARACTER_OF_TAG_NAME) {
throw err
@@ -144,7 +144,7 @@ describe('compiler: parse', () => {
})
test('lonly "{{" don\'t separate nodes', () => {
const ast = parse('a {{ b', {
const ast = baseParse('a {{ b', {
onError: error => {
if (error.code !== ErrorCodes.X_MISSING_INTERPOLATION_END) {
throw error
@@ -166,7 +166,7 @@ describe('compiler: parse', () => {
test('HTML entities compatibility in text (https://html.spec.whatwg.org/multipage/parsing.html#named-character-reference-state).', () => {
const spy = jest.fn()
const ast = parse('&ampersand;', {
const ast = baseParse('&ampersand;', {
namedCharacterReferences: { amp: '&' },
onError: spy
})
@@ -195,7 +195,7 @@ describe('compiler: parse', () => {
test('HTML entities compatibility in attribute (https://html.spec.whatwg.org/multipage/parsing.html#named-character-reference-state).', () => {
const spy = jest.fn()
const ast = parse(
const ast = baseParse(
'<div a="&ampersand;" b="&amp;ersand;" c="&amp!"></div>',
{
namedCharacterReferences: { amp: '&', 'amp;': '&' },
@@ -248,7 +248,7 @@ describe('compiler: parse', () => {
test('Some control character reference should be replaced.', () => {
const spy = jest.fn()
const ast = parse('&#x86;', { onError: spy })
const ast = baseParse('&#x86;', { onError: spy })
const text = ast.children[0] as TextNode
expect(text).toStrictEqual({
@@ -275,7 +275,7 @@ describe('compiler: parse', () => {
describe('Interpolation', () => {
test('simple interpolation', () => {
const ast = parse('{{message}}')
const ast = baseParse('{{message}}')
const interpolation = ast.children[0] as InterpolationNode
expect(interpolation).toStrictEqual({
@@ -300,7 +300,7 @@ describe('compiler: parse', () => {
})
test('it can have tag-like notation', () => {
const ast = parse('{{ a<b }}')
const ast = baseParse('{{ a<b }}')
const interpolation = ast.children[0] as InterpolationNode
expect(interpolation).toStrictEqual({
@@ -325,7 +325,7 @@ describe('compiler: parse', () => {
})
test('it can have tag-like notation (2)', () => {
const ast = parse('{{ a<b }}{{ c>d }}')
const ast = baseParse('{{ a<b }}{{ c>d }}')
const interpolation1 = ast.children[0] as InterpolationNode
const interpolation2 = ast.children[1] as InterpolationNode
@@ -371,7 +371,7 @@ describe('compiler: parse', () => {
})
test('it can have tag-like notation (3)', () => {
const ast = parse('<div>{{ "</div>" }}</div>')
const ast = baseParse('<div>{{ "</div>" }}</div>')
const element = ast.children[0] as ElementNode
const interpolation = element.children[0] as InterpolationNode
@@ -398,7 +398,7 @@ describe('compiler: parse', () => {
})
test('custom delimiters', () => {
const ast = parse('<p>{msg}</p>', {
const ast = baseParse('<p>{msg}</p>', {
delimiters: ['{', '}']
})
const element = ast.children[0] as ElementNode
@@ -428,7 +428,7 @@ describe('compiler: parse', () => {
describe('Comment', () => {
test('empty comment', () => {
const ast = parse('<!---->')
const ast = baseParse('<!---->')
const comment = ast.children[0] as CommentNode
expect(comment).toStrictEqual({
@@ -443,7 +443,7 @@ describe('compiler: parse', () => {
})
test('simple comment', () => {
const ast = parse('<!--abc-->')
const ast = baseParse('<!--abc-->')
const comment = ast.children[0] as CommentNode
expect(comment).toStrictEqual({
@@ -458,7 +458,7 @@ describe('compiler: parse', () => {
})
test('two comments', () => {
const ast = parse('<!--abc--><!--def-->')
const ast = baseParse('<!--abc--><!--def-->')
const comment1 = ast.children[0] as CommentNode
const comment2 = ast.children[1] as CommentNode
@@ -485,7 +485,7 @@ describe('compiler: parse', () => {
describe('Element', () => {
test('simple div', () => {
const ast = parse('<div>hello</div>')
const ast = baseParse('<div>hello</div>')
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
@@ -516,7 +516,7 @@ describe('compiler: parse', () => {
})
test('empty', () => {
const ast = parse('<div></div>')
const ast = baseParse('<div></div>')
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
@@ -526,7 +526,6 @@ describe('compiler: parse', () => {
tagType: ElementTypes.ELEMENT,
codegenNode: undefined,
props: [],
isSelfClosing: false,
children: [],
loc: {
@@ -538,7 +537,7 @@ describe('compiler: parse', () => {
})
test('self closing', () => {
const ast = parse('<div/>after')
const ast = baseParse('<div/>after')
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
@@ -560,7 +559,7 @@ describe('compiler: parse', () => {
})
test('void element', () => {
const ast = parse('<img>after', {
const ast = baseParse('<img>after', {
isVoidTag: tag => tag === 'img'
})
const element = ast.children[0] as ElementNode
@@ -583,8 +582,26 @@ describe('compiler: parse', () => {
})
})
test('template element with directives', () => {
const ast = baseParse('<template v-if="ok"></template>')
const element = ast.children[0]
expect(element).toMatchObject({
type: NodeTypes.ELEMENT,
tagType: ElementTypes.TEMPLATE
})
})
test('template element without directives', () => {
const ast = baseParse('<template></template>')
const element = ast.children[0]
expect(element).toMatchObject({
type: NodeTypes.ELEMENT,
tagType: ElementTypes.ELEMENT
})
})
test('native element with `isNativeTag`', () => {
const ast = parse('<div></div><comp></comp><Comp></Comp>', {
const ast = baseParse('<div></div><comp></comp><Comp></Comp>', {
isNativeTag: tag => tag === 'div'
})
@@ -608,7 +625,7 @@ describe('compiler: parse', () => {
})
test('native element without `isNativeTag`', () => {
const ast = parse('<div></div><comp></comp><Comp></Comp>')
const ast = baseParse('<div></div><comp></comp><Comp></Comp>')
expect(ast.children[0]).toMatchObject({
type: NodeTypes.ELEMENT,
@@ -629,8 +646,57 @@ describe('compiler: parse', () => {
})
})
test('v-is without `isNativeTag`', () => {
const ast = baseParse(
`<div></div><div v-is="'foo'"></div><Comp></Comp>`,
{
isNativeTag: tag => tag === 'div'
}
)
expect(ast.children[0]).toMatchObject({
type: NodeTypes.ELEMENT,
tag: 'div',
tagType: ElementTypes.ELEMENT
})
expect(ast.children[1]).toMatchObject({
type: NodeTypes.ELEMENT,
tag: 'div',
tagType: ElementTypes.COMPONENT
})
expect(ast.children[2]).toMatchObject({
type: NodeTypes.ELEMENT,
tag: 'Comp',
tagType: ElementTypes.COMPONENT
})
})
test('v-is with `isNativeTag`', () => {
const ast = baseParse(`<div></div><div v-is="'foo'"></div><Comp></Comp>`)
expect(ast.children[0]).toMatchObject({
type: NodeTypes.ELEMENT,
tag: 'div',
tagType: ElementTypes.ELEMENT
})
expect(ast.children[1]).toMatchObject({
type: NodeTypes.ELEMENT,
tag: 'div',
tagType: ElementTypes.COMPONENT
})
expect(ast.children[2]).toMatchObject({
type: NodeTypes.ELEMENT,
tag: 'Comp',
tagType: ElementTypes.COMPONENT
})
})
test('custom element', () => {
const ast = parse('<div></div><comp></comp>', {
const ast = baseParse('<div></div><comp></comp>', {
isNativeTag: tag => tag === 'div',
isCustomElement: tag => tag === 'comp'
})
@@ -649,7 +715,7 @@ describe('compiler: parse', () => {
})
test('attribute with no value', () => {
const ast = parse('<div id></div>')
const ast = baseParse('<div id></div>')
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
@@ -682,7 +748,7 @@ describe('compiler: parse', () => {
})
test('attribute with empty value, double quote', () => {
const ast = parse('<div id=""></div>')
const ast = baseParse('<div id=""></div>')
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
@@ -723,7 +789,7 @@ describe('compiler: parse', () => {
})
test('attribute with empty value, single quote', () => {
const ast = parse("<div id=''></div>")
const ast = baseParse("<div id=''></div>")
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
@@ -764,7 +830,7 @@ describe('compiler: parse', () => {
})
test('attribute with value, double quote', () => {
const ast = parse('<div id=">\'"></div>')
const ast = baseParse('<div id=">\'"></div>')
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
@@ -805,7 +871,7 @@ describe('compiler: parse', () => {
})
test('attribute with value, single quote', () => {
const ast = parse("<div id='>\"'></div>")
const ast = baseParse("<div id='>\"'></div>")
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
@@ -846,7 +912,7 @@ describe('compiler: parse', () => {
})
test('attribute with value, unquoted', () => {
const ast = parse('<div id=a/></div>')
const ast = baseParse('<div id=a/></div>')
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
@@ -887,7 +953,7 @@ describe('compiler: parse', () => {
})
test('multiple attributes', () => {
const ast = parse('<div id=a class="c" inert style=\'\'></div>')
const ast = baseParse('<div id=a class="c" inert style=\'\'></div>')
const element = ast.children[0] as ElementNode
expect(element).toStrictEqual({
@@ -974,7 +1040,7 @@ describe('compiler: parse', () => {
})
test('directive with no value', () => {
const ast = parse('<div v-if/>')
const ast = baseParse('<div v-if/>')
const directive = (ast.children[0] as ElementNode).props[0]
expect(directive).toStrictEqual({
@@ -992,7 +1058,7 @@ describe('compiler: parse', () => {
})
test('directive with value', () => {
const ast = parse('<div v-if="a"/>')
const ast = baseParse('<div v-if="a"/>')
const directive = (ast.children[0] as ElementNode).props[0]
expect(directive).toStrictEqual({
@@ -1020,7 +1086,7 @@ describe('compiler: parse', () => {
})
test('directive with argument', () => {
const ast = parse('<div v-on:click/>')
const ast = baseParse('<div v-on:click/>')
const directive = (ast.children[0] as ElementNode).props[0]
expect(directive).toStrictEqual({
@@ -1057,7 +1123,7 @@ describe('compiler: parse', () => {
})
test('directive with a modifier', () => {
const ast = parse('<div v-on.enter/>')
const ast = baseParse('<div v-on.enter/>')
const directive = (ast.children[0] as ElementNode).props[0]
expect(directive).toStrictEqual({
@@ -1075,7 +1141,7 @@ describe('compiler: parse', () => {
})
test('directive with two modifiers', () => {
const ast = parse('<div v-on.enter.exact/>')
const ast = baseParse('<div v-on.enter.exact/>')
const directive = (ast.children[0] as ElementNode).props[0]
expect(directive).toStrictEqual({
@@ -1093,7 +1159,7 @@ describe('compiler: parse', () => {
})
test('directive with argument and modifiers', () => {
const ast = parse('<div v-on:click.enter.exact/>')
const ast = baseParse('<div v-on:click.enter.exact/>')
const directive = (ast.children[0] as ElementNode).props[0]
expect(directive).toStrictEqual({
@@ -1130,7 +1196,7 @@ describe('compiler: parse', () => {
})
test('v-bind shorthand', () => {
const ast = parse('<div :a=b />')
const ast = baseParse('<div :a=b />')
const directive = (ast.children[0] as ElementNode).props[0]
expect(directive).toStrictEqual({
@@ -1178,7 +1244,7 @@ describe('compiler: parse', () => {
})
test('v-bind shorthand with modifier', () => {
const ast = parse('<div :a.sync=b />')
const ast = baseParse('<div :a.sync=b />')
const directive = (ast.children[0] as ElementNode).props[0]
expect(directive).toStrictEqual({
@@ -1226,7 +1292,7 @@ describe('compiler: parse', () => {
})
test('v-on shorthand', () => {
const ast = parse('<div @a=b />')
const ast = baseParse('<div @a=b />')
const directive = (ast.children[0] as ElementNode).props[0]
expect(directive).toStrictEqual({
@@ -1274,7 +1340,7 @@ describe('compiler: parse', () => {
})
test('v-on shorthand with modifier', () => {
const ast = parse('<div @a.enter=b />')
const ast = baseParse('<div @a.enter=b />')
const directive = (ast.children[0] as ElementNode).props[0]
expect(directive).toStrictEqual({
@@ -1322,7 +1388,7 @@ describe('compiler: parse', () => {
})
test('v-slot shorthand', () => {
const ast = parse('<Comp #a="{ b }" />')
const ast = baseParse('<Comp #a="{ b }" />')
const directive = (ast.children[0] as ElementNode).props[0]
expect(directive).toStrictEqual({
@@ -1369,7 +1435,7 @@ describe('compiler: parse', () => {
})
test('v-pre', () => {
const ast = parse(
const ast = baseParse(
`<div v-pre :id="foo"><Comp/>{{ bar }}</div>\n` +
`<div :id="foo"><Comp/>{{ bar }}</div>`
)
@@ -1451,7 +1517,7 @@ describe('compiler: parse', () => {
})
test('end tags are case-insensitive.', () => {
const ast = parse('<div>hello</DIV>after')
const ast = baseParse('<div>hello</DIV>after')
const element = ast.children[0] as ElementNode
const text = element.children[0] as TextNode
@@ -1468,14 +1534,14 @@ describe('compiler: parse', () => {
})
test('self closing single tag', () => {
const ast = parse('<div :class="{ some: condition }" />')
const ast = baseParse('<div :class="{ some: condition }" />')
expect(ast.children).toHaveLength(1)
expect(ast.children[0]).toMatchObject({ tag: 'div' })
})
test('self closing multiple tag', () => {
const ast = parse(
const ast = baseParse(
`<div :class="{ some: condition }" />\n` +
`<p v-bind:style="{ color: 'red' }"/>`
)
@@ -1488,7 +1554,7 @@ describe('compiler: parse', () => {
})
test('valid html', () => {
const ast = parse(
const ast = baseParse(
`<div :class="{ some: condition }">\n` +
` <p v-bind:style="{ color: 'red' }"/>\n` +
` <!-- a comment with <html> inside it -->\n` +
@@ -1513,11 +1579,11 @@ describe('compiler: parse', () => {
test('invalid html', () => {
expect(() => {
parse(`<div>\n<span>\n</div>\n</span>`)
}).toThrow('End tag was not found. (3:1)')
baseParse(`<div>\n<span>\n</div>\n</span>`)
}).toThrow('Element is missing end tag.')
const spy = jest.fn()
const ast = parse(`<div>\n<span>\n</div>\n</span>`, {
const ast = baseParse(`<div>\n<span>\n</div>\n</span>`, {
onError: spy
})
@@ -1527,8 +1593,8 @@ describe('compiler: parse', () => {
code: ErrorCodes.X_MISSING_END_TAG,
loc: {
start: {
offset: 13,
line: 3,
offset: 6,
line: 2,
column: 1
}
}
@@ -1552,7 +1618,7 @@ describe('compiler: parse', () => {
})
test('parse with correct location info', () => {
const [foo, bar, but, baz] = parse(
const [foo, bar, but, baz] = baseParse(
`
foo
is {{ bar }} but {{ baz }}`.trim()
@@ -1588,7 +1654,7 @@ foo
describe('namedCharacterReferences option', () => {
test('use the given map', () => {
const ast: any = parse('&amp;&cups;', {
const ast: any = baseParse('&amp;&cups;', {
namedCharacterReferences: {
'cups;': '\u222A\uFE00' // UNION with serifs
},
@@ -1603,18 +1669,18 @@ foo
describe('whitespace management', () => {
it('should remove whitespaces at start/end inside an element', () => {
const ast = parse(`<div> <span/> </div>`)
const ast = baseParse(`<div> <span/> </div>`)
expect((ast.children[0] as ElementNode).children.length).toBe(1)
})
it('should remove whitespaces w/ newline between elements', () => {
const ast = parse(`<div/> \n <div/> \n <div/>`)
const ast = baseParse(`<div/> \n <div/> \n <div/>`)
expect(ast.children.length).toBe(3)
expect(ast.children.every(c => c.type === NodeTypes.ELEMENT)).toBe(true)
})
it('should remove whitespaces adjacent to comments', () => {
const ast = parse(`<div/> \n <!--foo--> <div/>`)
const ast = baseParse(`<div/> \n <!--foo--> <div/>`)
expect(ast.children.length).toBe(3)
expect(ast.children[0].type).toBe(NodeTypes.ELEMENT)
expect(ast.children[1].type).toBe(NodeTypes.COMMENT)
@@ -1622,7 +1688,7 @@ foo
})
it('should remove whitespaces w/ newline between comments and elements', () => {
const ast = parse(`<div/> \n <!--foo--> \n <div/>`)
const ast = baseParse(`<div/> \n <!--foo--> \n <div/>`)
expect(ast.children.length).toBe(3)
expect(ast.children[0].type).toBe(NodeTypes.ELEMENT)
expect(ast.children[1].type).toBe(NodeTypes.COMMENT)
@@ -1630,7 +1696,7 @@ foo
})
it('should NOT remove whitespaces w/ newline between interpolations', () => {
const ast = parse(`{{ foo }} \n {{ bar }}`)
const ast = baseParse(`{{ foo }} \n {{ bar }}`)
expect(ast.children.length).toBe(3)
expect(ast.children[0].type).toBe(NodeTypes.INTERPOLATION)
expect(ast.children[1]).toMatchObject({
@@ -1641,7 +1707,7 @@ foo
})
it('should NOT remove whitespaces w/o newline between elements', () => {
const ast = parse(`<div/> <div/> <div/>`)
const ast = baseParse(`<div/> <div/> <div/>`)
expect(ast.children.length).toBe(5)
expect(ast.children.map(c => c.type)).toMatchObject([
NodeTypes.ELEMENT,
@@ -1653,7 +1719,7 @@ foo
})
it('should condense consecutive whitespaces in text', () => {
const ast = parse(` foo \n bar baz `)
const ast = baseParse(` foo \n bar baz `)
expect((ast.children[0] as TextNode).content).toBe(` foo bar baz `)
})
})
@@ -1833,7 +1899,7 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 11, line: 1, column: 12 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -1846,7 +1912,7 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 12, line: 1, column: 13 }
loc: { offset: 0, line: 1, column: 1 }
}
]
}
@@ -1861,11 +1927,11 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 29, line: 1, column: 30 }
loc: { offset: 10, line: 1, column: 11 }
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 29, line: 1, column: 30 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -1878,11 +1944,11 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 24, line: 1, column: 25 }
loc: { offset: 10, line: 1, column: 11 }
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 24, line: 1, column: 25 }
loc: { offset: 0, line: 1, column: 1 }
}
]
}
@@ -1897,7 +1963,7 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 21, line: 1, column: 22 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -1910,7 +1976,7 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 14, line: 1, column: 15 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -1925,7 +1991,7 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 12, line: 1, column: 13 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -1938,7 +2004,7 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 13, line: 1, column: 14 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -1951,7 +2017,7 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 15, line: 1, column: 16 }
loc: { offset: 0, line: 1, column: 1 }
}
]
}
@@ -1962,7 +2028,7 @@ foo
errors: [
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 32, line: 1, column: 33 }
loc: { offset: 0, line: 1, column: 1 }
},
{
type: ErrorCodes.EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT,
@@ -1975,7 +2041,7 @@ foo
errors: [
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 28, line: 1, column: 29 }
loc: { offset: 0, line: 1, column: 1 }
}
]
}
@@ -1990,11 +2056,11 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 14, line: 1, column: 15 }
loc: { offset: 10, line: 1, column: 11 }
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 14, line: 1, column: 15 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -2007,11 +2073,11 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 15, line: 1, column: 16 }
loc: { offset: 10, line: 1, column: 11 }
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 15, line: 1, column: 16 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -2024,11 +2090,11 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 17, line: 1, column: 18 }
loc: { offset: 10, line: 1, column: 11 }
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 17, line: 1, column: 18 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -2041,11 +2107,11 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 18, line: 1, column: 19 }
loc: { offset: 10, line: 1, column: 11 }
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 18, line: 1, column: 19 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -2062,11 +2128,11 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 19, line: 1, column: 20 }
loc: { offset: 10, line: 1, column: 11 }
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 19, line: 1, column: 20 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -2079,11 +2145,11 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 22, line: 1, column: 23 }
loc: { offset: 10, line: 1, column: 11 }
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 22, line: 1, column: 23 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -2096,11 +2162,11 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 22, line: 1, column: 23 }
loc: { offset: 10, line: 1, column: 11 }
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 22, line: 1, column: 23 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -2113,11 +2179,11 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 23, line: 1, column: 24 }
loc: { offset: 10, line: 1, column: 11 }
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 23, line: 1, column: 24 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -2130,11 +2196,11 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 23, line: 1, column: 24 }
loc: { offset: 10, line: 1, column: 11 }
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 23, line: 1, column: 24 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -2147,11 +2213,11 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 21, line: 1, column: 22 }
loc: { offset: 10, line: 1, column: 11 }
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 21, line: 1, column: 22 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -2168,11 +2234,11 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 24, line: 1, column: 25 }
loc: { offset: 10, line: 1, column: 11 }
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 24, line: 1, column: 25 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -2189,11 +2255,11 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 24, line: 1, column: 25 }
loc: { offset: 10, line: 1, column: 11 }
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 24, line: 1, column: 25 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -2210,11 +2276,11 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 23, line: 1, column: 24 }
loc: { offset: 10, line: 1, column: 11 }
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 23, line: 1, column: 24 }
loc: { offset: 0, line: 1, column: 1 }
}
]
}
@@ -2292,7 +2358,7 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 27, line: 1, column: 28 }
loc: { offset: 0, line: 1, column: 1 }
}
]
},
@@ -2429,7 +2495,7 @@ foo
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 19, line: 1, column: 20 }
loc: { offset: 0, line: 1, column: 1 }
}
]
}
@@ -2594,17 +2660,6 @@ foo
]
}
],
UNKNOWN_NAMED_CHARACTER_REFERENCE: [
{
code: '<template>&unknown;</template>',
errors: [
{
type: ErrorCodes.UNKNOWN_NAMED_CHARACTER_REFERENCE,
loc: { offset: 10, line: 1, column: 11 }
}
]
}
],
X_INVALID_END_TAG: [
{
code: '<template></div></template>',
@@ -2651,7 +2706,7 @@ foo
errors: [
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 15, line: 1, column: 16 }
loc: { offset: 10, line: 1, column: 11 }
}
]
},
@@ -2660,11 +2715,11 @@ foo
errors: [
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 15, line: 1, column: 16 }
loc: { offset: 10, line: 1, column: 11 }
},
{
type: ErrorCodes.X_MISSING_END_TAG,
loc: { offset: 15, line: 1, column: 16 }
loc: { offset: 0, line: 1, column: 1 }
}
]
}
@@ -2716,7 +2771,7 @@ foo
),
() => {
const spy = jest.fn()
const ast = parse(code, {
const ast = baseParse(code, {
getNamespace: (tag, parent) => {
const ns = parent ? parent.ns : Namespaces.HTML
if (ns === Namespaces.HTML) {

View File

@@ -0,0 +1,97 @@
import { baseCompile } from '../src/compile'
import {
WITH_SCOPE_ID,
PUSH_SCOPE_ID,
POP_SCOPE_ID
} from '../src/runtimeHelpers'
import { PatchFlags } from '@vue/shared'
import { genFlagText } from './testUtils'
describe('scopeId compiler support', () => {
test('should only work in module mode', () => {
expect(() => {
baseCompile(``, { scopeId: 'test' })
}).toThrow(`"scopeId" option is only supported in module mode`)
})
test('should wrap render function', () => {
const { ast, code } = baseCompile(`<div/>`, {
mode: 'module',
scopeId: 'test'
})
expect(ast.helpers).toContain(WITH_SCOPE_ID)
expect(code).toMatch(`const _withId = _withScopeId("test")`)
expect(code).toMatch(`export const render = _withId(function render(`)
expect(code).toMatchSnapshot()
})
test('should wrap default slot', () => {
const { code } = baseCompile(`<Child><div/></Child>`, {
mode: 'module',
scopeId: 'test'
})
expect(code).toMatch(`default: _withId(() => [`)
expect(code).toMatchSnapshot()
})
test('should wrap named slots', () => {
const { code } = baseCompile(
`<Child>
<template #foo="{ msg }">{{ msg }}</template>
<template #bar><div/></template>
</Child>
`,
{
mode: 'module',
scopeId: 'test'
}
)
expect(code).toMatch(`foo: _withId(({ msg }) => [`)
expect(code).toMatch(`bar: _withId(() => [`)
expect(code).toMatchSnapshot()
})
test('should wrap dynamic slots', () => {
const { code } = baseCompile(
`<Child>
<template #foo v-if="ok"><div/></template>
<template v-for="i in list" #[i]><div/></template>
</Child>
`,
{
mode: 'module',
scopeId: 'test'
}
)
expect(code).toMatch(/name: "foo",\s+fn: _withId\(/)
expect(code).toMatch(/name: i,\s+fn: _withId\(/)
expect(code).toMatchSnapshot()
})
test('should push scopeId for hoisted nodes', () => {
const { ast, code } = baseCompile(
`<div><div>hello</div>{{ foo }}<div>world</div></div>`,
{
mode: 'module',
scopeId: 'test',
hoistStatic: true
}
)
expect(ast.helpers).toContain(PUSH_SCOPE_ID)
expect(ast.helpers).toContain(POP_SCOPE_ID)
expect(ast.hoists.length).toBe(2)
expect(code).toMatch(
[
`_pushScopeId("test")`,
`const _hoisted_1 = _createVNode("div", null, "hello", ${genFlagText(
PatchFlags.HOISTED
)})`,
`const _hoisted_2 = _createVNode("div", null, "world", ${genFlagText(
PatchFlags.HOISTED
)})`,
`_popScopeId()`
].join('\n')
)
expect(code).toMatchSnapshot()
})
})

View File

@@ -4,9 +4,8 @@ import {
locStub,
Namespaces,
ElementTypes,
PlainElementCodegenNode
VNodeCall
} from '../src'
import { CREATE_VNODE } from '../src/runtimeHelpers'
import { isString, PatchFlags, PatchFlagNames, isArray } from '@vue/shared'
const leadingBracketRE = /^\[/
@@ -39,7 +38,11 @@ export function createObjectMatcher(obj: Record<string, any>) {
}
export function createElementWithCodegen(
args: PlainElementCodegenNode['arguments']
tag: VNodeCall['tag'],
props?: VNodeCall['props'],
children?: VNodeCall['children'],
patchFlag?: VNodeCall['patchFlag'],
dynamicProps?: VNodeCall['dynamicProps']
): ElementNode {
return {
type: NodeTypes.ELEMENT,
@@ -51,10 +54,16 @@ export function createElementWithCodegen(
props: [],
children: [],
codegenNode: {
type: NodeTypes.JS_CALL_EXPRESSION,
loc: locStub,
callee: CREATE_VNODE,
arguments: args
type: NodeTypes.VNODE_CALL,
tag,
props,
children,
patchFlag,
dynamicProps,
directives: undefined,
isBlock: false,
isForBlock: false,
loc: locStub
}
}
}

View File

@@ -1,19 +1,17 @@
import { parse } from '../src/parse'
import { baseParse } from '../src/parse'
import { transform, NodeTransform } from '../src/transform'
import {
ElementNode,
NodeTypes,
DirectiveNode,
ExpressionNode
ExpressionNode,
VNodeCall
} from '../src/ast'
import { ErrorCodes, createCompilerError } from '../src/errors'
import {
TO_STRING,
OPEN_BLOCK,
CREATE_BLOCK,
TO_DISPLAY_STRING,
FRAGMENT,
RENDER_SLOT,
WITH_DIRECTIVES,
CREATE_COMMENT
} from '../src/runtimeHelpers'
import { transformIf } from '../src/transforms/vIf'
@@ -26,7 +24,7 @@ import { PatchFlags } from '@vue/shared'
describe('compiler: transform', () => {
test('context state', () => {
const ast = parse(`<div>hello {{ world }}</div>`)
const ast = baseParse(`<div>hello {{ world }}</div>`)
// manually store call arguments because context is mutable and shared
// across calls
@@ -72,7 +70,7 @@ describe('compiler: transform', () => {
})
test('context.replaceNode', () => {
const ast = parse(`<div/><span/>`)
const ast = baseParse(`<div/><span/>`)
const plugin: NodeTransform = (node, context) => {
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
// change the node to <p>
@@ -106,7 +104,7 @@ describe('compiler: transform', () => {
})
test('context.removeNode', () => {
const ast = parse(`<span/><div>hello</div><span/>`)
const ast = baseParse(`<span/><div>hello</div><span/>`)
const c1 = ast.children[0]
const c2 = ast.children[2]
@@ -132,7 +130,7 @@ describe('compiler: transform', () => {
})
test('context.removeNode (prev sibling)', () => {
const ast = parse(`<span/><div/><span/>`)
const ast = baseParse(`<span/><div/><span/>`)
const c1 = ast.children[0]
const c2 = ast.children[2]
@@ -159,7 +157,7 @@ describe('compiler: transform', () => {
})
test('context.removeNode (next sibling)', () => {
const ast = parse(`<span/><div/><span/>`)
const ast = baseParse(`<span/><div/><span/>`)
const c1 = ast.children[0]
const d1 = ast.children[1]
@@ -186,7 +184,7 @@ describe('compiler: transform', () => {
})
test('context.hoist', () => {
const ast = parse(`<div :id="foo"/><div :id="bar"/>`)
const ast = baseParse(`<div :id="foo"/><div :id="bar"/>`)
const hoisted: ExpressionNode[] = []
const mock: NodeTransform = (node, context) => {
if (node.type === NodeTypes.ELEMENT) {
@@ -204,7 +202,7 @@ describe('compiler: transform', () => {
})
test('onError option', () => {
const ast = parse(`<div/>`)
const ast = baseParse(`<div/>`)
const loc = ast.children[0].loc
const plugin: NodeTransform = (node, context) => {
context.onError(
@@ -225,20 +223,20 @@ describe('compiler: transform', () => {
})
test('should inject toString helper for interpolations', () => {
const ast = parse(`{{ foo }}`)
const ast = baseParse(`{{ foo }}`)
transform(ast, {})
expect(ast.helpers).toContain(TO_STRING)
expect(ast.helpers).toContain(TO_DISPLAY_STRING)
})
test('should inject createVNode and Comment for comments', () => {
const ast = parse(`<!--foo-->`)
const ast = baseParse(`<!--foo-->`)
transform(ast, {})
expect(ast.helpers).toContain(CREATE_COMMENT)
})
describe('root codegenNode', () => {
function transformWithCodegen(template: string) {
const ast = parse(template)
const ast = baseParse(template)
transform(ast, {
nodeTransforms: [
transformIf,
@@ -251,20 +249,19 @@ describe('compiler: transform', () => {
return ast
}
function createBlockMatcher(args: any[]) {
function createBlockMatcher(
tag: VNodeCall['tag'],
props?: VNodeCall['props'],
children?: VNodeCall['children'],
patchFlag?: VNodeCall['patchFlag']
) {
return {
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
expressions: [
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: OPEN_BLOCK
},
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_BLOCK,
arguments: args
}
]
type: NodeTypes.VNODE_CALL,
isBlock: true,
tag,
props,
children,
patchFlag
}
}
@@ -285,7 +282,7 @@ describe('compiler: transform', () => {
test('single element', () => {
const ast = transformWithCodegen(`<div/>`)
expect(ast.codegenNode).toMatchObject(createBlockMatcher([`"div"`]))
expect(ast.codegenNode).toMatchObject(createBlockMatcher(`"div"`))
})
test('root v-if', () => {
@@ -305,22 +302,8 @@ describe('compiler: transform', () => {
test('root element with custom directive', () => {
const ast = transformWithCodegen(`<div v-foo/>`)
expect(ast.codegenNode).toMatchObject({
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
expressions: [
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: OPEN_BLOCK
},
{
type: NodeTypes.JS_CALL_EXPRESSION,
// should wrap withDirectives() around createBlock()
callee: WITH_DIRECTIVES,
arguments: [
{ callee: CREATE_BLOCK },
{ type: NodeTypes.JS_ARRAY_EXPRESSION }
]
}
]
type: NodeTypes.VNODE_CALL,
directives: { type: NodeTypes.JS_ARRAY_EXPRESSION }
})
})
@@ -348,15 +331,15 @@ describe('compiler: transform', () => {
test('multiple children', () => {
const ast = transformWithCodegen(`<div/><div/>`)
expect(ast.codegenNode).toMatchObject(
createBlockMatcher([
createBlockMatcher(
FRAGMENT,
`null`,
undefined,
[
{ type: NodeTypes.ELEMENT, tag: `div` },
{ type: NodeTypes.ELEMENT, tag: `div` }
],
] as any,
genFlagText(PatchFlags.STABLE_FRAGMENT)
])
)
)
})
})

View File

@@ -2,14 +2,14 @@
exports[`compiler: hoistStatic transform hoist element with static key 1`] = `
"const _Vue = Vue
const { createVNode: _createVNode } = Vue
const { createVNode: _createVNode } = _Vue
const _hoisted_1 = _createVNode(\\"div\\", { key: \\"foo\\" })
const _hoisted_1 = _createVNode(\\"div\\", { key: \\"foo\\" }, null, -1 /* HOISTED */)
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return function render() {
with (this) {
const { createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
_hoisted_1
]))
@@ -19,17 +19,17 @@ return function render() {
exports[`compiler: hoistStatic transform hoist nested static tree 1`] = `
"const _Vue = Vue
const { createVNode: _createVNode } = Vue
const { createVNode: _createVNode } = _Vue
const _hoisted_1 = _createVNode(\\"p\\", null, [
_createVNode(\\"span\\"),
_createVNode(\\"span\\")
])
], -1 /* HOISTED */)
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return function render() {
with (this) {
const { createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
_hoisted_1
]))
@@ -39,16 +39,16 @@ return function render() {
exports[`compiler: hoistStatic transform hoist nested static tree with comments 1`] = `
"const _Vue = Vue
const { createVNode: _createVNode, createCommentVNode: _createCommentVNode } = Vue
const { createVNode: _createVNode, createCommentVNode: _createCommentVNode } = _Vue
const _hoisted_1 = _createVNode(\\"div\\", null, [
_createCommentVNode(\\"comment\\")
])
], -1 /* HOISTED */)
return function render(_ctx, _cache) {
with (_ctx) {
const { createCommentVNode: _createCommentVNode, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return function render() {
with (this) {
const { createCommentVNode: _createCommentVNode, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
_hoisted_1
]))
@@ -58,15 +58,15 @@ return function render() {
exports[`compiler: hoistStatic transform hoist siblings with common non-hoistable parent 1`] = `
"const _Vue = Vue
const { createVNode: _createVNode } = Vue
const { createVNode: _createVNode } = _Vue
const _hoisted_1 = _createVNode(\\"span\\")
const _hoisted_2 = _createVNode(\\"div\\")
const _hoisted_1 = _createVNode(\\"span\\", null, null, -1 /* HOISTED */)
const _hoisted_2 = _createVNode(\\"div\\", null, null, -1 /* HOISTED */)
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return function render() {
with (this) {
const { createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
_hoisted_1,
_hoisted_2
@@ -77,14 +77,14 @@ return function render() {
exports[`compiler: hoistStatic transform hoist simple element 1`] = `
"const _Vue = Vue
const { createVNode: _createVNode } = Vue
const { createVNode: _createVNode } = _Vue
const _hoisted_1 = _createVNode(\\"span\\", { class: \\"inline\\" }, \\"hello\\")
const _hoisted_1 = _createVNode(\\"span\\", { class: \\"inline\\" }, \\"hello\\", -1 /* HOISTED */)
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return function render() {
with (this) {
const { createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
_hoisted_1
]))
@@ -94,18 +94,18 @@ return function render() {
exports[`compiler: hoistStatic transform hoist static props for elements with directives 1`] = `
"const _Vue = Vue
const { createVNode: _createVNode } = Vue
const { createVNode: _createVNode } = _Vue
const _hoisted_1 = { id: \\"foo\\" }
return function render() {
with (this) {
const { createVNode: _createVNode, withDirectives: _withDirectives, resolveDirective: _resolveDirective, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { resolveDirective: _resolveDirective, createVNode: _createVNode, withDirectives: _withDirectives, openBlock: _openBlock, createBlock: _createBlock } = _Vue
const _directive_foo = _resolveDirective(\\"foo\\")
return (_openBlock(), _createBlock(\\"div\\", null, [
_withDirectives(_createVNode(\\"div\\", _hoisted_1, null, 32 /* NEED_PATCH */), [
_withDirectives(_createVNode(\\"div\\", _hoisted_1, null, 512 /* NEED_PATCH */), [
[_directive_foo]
])
]))
@@ -115,16 +115,16 @@ return function render() {
exports[`compiler: hoistStatic transform hoist static props for elements with dynamic text children 1`] = `
"const _Vue = Vue
const { createVNode: _createVNode } = Vue
const { createVNode: _createVNode } = _Vue
const _hoisted_1 = { id: \\"foo\\" }
return function render() {
with (this) {
const { toString: _toString, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
_createVNode(\\"div\\", _hoisted_1, _toString(hello), 1 /* TEXT */)
_createVNode(\\"div\\", _hoisted_1, _toDisplayString(hello), 1 /* TEXT */)
]))
}
}"
@@ -132,16 +132,16 @@ return function render() {
exports[`compiler: hoistStatic transform hoist static props for elements with unhoistable children 1`] = `
"const _Vue = Vue
const { createVNode: _createVNode } = Vue
const { createVNode: _createVNode } = _Vue
const _hoisted_1 = { id: \\"foo\\" }
return function render() {
with (this) {
const { resolveComponent: _resolveComponent, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(\\"div\\", null, [
_createVNode(\\"div\\", _hoisted_1, [
_createVNode(_component_Comp)
@@ -153,16 +153,16 @@ return function render() {
exports[`compiler: hoistStatic transform prefixIdentifiers hoist class with static object value 1`] = `
"const _Vue = Vue
const { createVNode: _createVNode } = Vue
const { createVNode: _createVNode } = _Vue
const _hoisted_1 = { class: { foo: true } }
return function render() {
with (this) {
const { toString: _toString, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
_createVNode(\\"span\\", _hoisted_1, _toString(_ctx.bar), 1 /* TEXT */)
_createVNode(\\"span\\", _hoisted_1, _toDisplayString(_ctx.bar), 1 /* TEXT */)
]))
}
}"
@@ -170,19 +170,14 @@ return function render() {
exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static tree with static interpolation 1`] = `
"const _Vue = Vue
const { createVNode: _createVNode } = Vue
const { createVNode: _createVNode } = _Vue
const _hoisted_1 = _createVNode(\\"span\\", null, [
\\"foo \\",
_toString(1),
\\" \\",
_toString(true)
])
const _hoisted_1 = _createVNode(\\"span\\", null, \\"foo \\" + _toDisplayString(1) + \\" \\" + _toDisplayString(true), -1 /* HOISTED */)
return function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return function render() {
with (this) {
const { toString: _toString, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
_hoisted_1
]))
@@ -192,14 +187,14 @@ return function render() {
exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static tree with static prop value 1`] = `
"const _Vue = Vue
const { createVNode: _createVNode } = Vue
const { createVNode: _createVNode } = _Vue
const _hoisted_1 = _createVNode(\\"span\\", { foo: 0 }, _toString(1))
const _hoisted_1 = _createVNode(\\"span\\", { foo: 0 }, _toDisplayString(1), -1 /* HOISTED */)
return function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return function render() {
with (this) {
const { toString: _toString, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
_hoisted_1
]))
@@ -208,14 +203,12 @@ return function render() {
`;
exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist elements with cached handlers 1`] = `
"import { createVNode, createBlock, openBlock } from \\"vue\\"
"import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
export default function render() {
const _ctx = this
const _cache = _ctx.$cache
return (openBlock(), createBlock(\\"div\\", null, [
createVNode(\\"div\\", null, [
createVNode(\\"div\\", {
export function render(_ctx, _cache) {
return (_openBlock(), _createBlock(\\"div\\", null, [
_createVNode(\\"div\\", null, [
_createVNode(\\"div\\", {
onClick: _cache[1] || (_cache[1] = $event => (_ctx.foo($event)))
})
])
@@ -226,14 +219,14 @@ export default function render() {
exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that refer scope variables (2) 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, toString: _toString, createVNode: _createVNode } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, toDisplayString: _toDisplayString, createVNode: _createVNode } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
(_openBlock(false), _createBlock(_Fragment, null, _renderList(_ctx.list, (o) => {
(_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.list, (o) => {
return (_openBlock(), _createBlock(\\"p\\", null, [
_createVNode(\\"span\\", null, _toString(o + 'foo'), 1 /* TEXT */)
_createVNode(\\"span\\", null, _toDisplayString(o + 'foo'), 1 /* TEXT */)
]))
}), 256 /* UNKEYED_FRAGMENT */))
]))
@@ -244,15 +237,17 @@ return function render() {
exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that refer scope variables (v-slot) 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { toString: _toString, resolveComponent: _resolveComponent, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(_component_Comp, null, {
default: ({ foo }) => [_toString(_ctx.foo)],
_compiled: true
default: _withCtx(({ foo }) => [
_createTextVNode(_toDisplayString(_ctx.foo), 1 /* TEXT */)
]),
_: 1
}))
}
}"
@@ -261,14 +256,14 @@ return function render() {
exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that refer scope variables 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, toString: _toString, createVNode: _createVNode } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, toDisplayString: _toDisplayString, createVNode: _createVNode } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
(_openBlock(false), _createBlock(_Fragment, null, _renderList(_ctx.list, (o) => {
(_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.list, (o) => {
return (_openBlock(), _createBlock(\\"p\\", null, [
_createVNode(\\"span\\", null, _toString(o), 1 /* TEXT */)
_createVNode(\\"span\\", null, _toDisplayString(o), 1 /* TEXT */)
]))
}), 256 /* UNKEYED_FRAGMENT */))
]))
@@ -279,12 +274,12 @@ return function render() {
exports[`compiler: hoistStatic transform should NOT hoist components 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { resolveComponent: _resolveComponent, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(\\"div\\", null, [
_createVNode(_component_Comp)
]))
@@ -295,12 +290,12 @@ return function render() {
exports[`compiler: hoistStatic transform should NOT hoist element with dynamic key 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
_createVNode(\\"div\\", { key: foo })
(_openBlock(), _createBlock(\\"div\\", { key: foo }))
]))
}
}"
@@ -309,10 +304,10 @@ return function render() {
exports[`compiler: hoistStatic transform should NOT hoist element with dynamic props 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
_createVNode(\\"div\\", { id: foo }, null, 8 /* PROPS */, [\\"id\\"])
]))
@@ -323,12 +318,12 @@ return function render() {
exports[`compiler: hoistStatic transform should NOT hoist element with dynamic ref 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
_createVNode(\\"div\\", { ref: foo }, null, 32 /* NEED_PATCH */)
_createVNode(\\"div\\", { ref: foo }, null, 512 /* NEED_PATCH */)
]))
}
}"
@@ -337,10 +332,10 @@ return function render() {
exports[`compiler: hoistStatic transform should NOT hoist root node 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(\\"div\\"))
}
}"
@@ -348,17 +343,17 @@ return function render() {
exports[`compiler: hoistStatic transform should hoist v-for children if static 1`] = `
"const _Vue = Vue
const { createVNode: _createVNode } = Vue
const { createVNode: _createVNode } = _Vue
const _hoisted_1 = { id: \\"foo\\" }
const _hoisted_2 = _createVNode(\\"span\\")
const _hoisted_2 = _createVNode(\\"span\\", null, null, -1 /* HOISTED */)
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode } = _Vue
return function render() {
with (this) {
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
(_openBlock(false), _createBlock(_Fragment, null, _renderList(list, (i) => {
(_openBlock(true), _createBlock(_Fragment, null, _renderList(list, (i) => {
return (_openBlock(), _createBlock(\\"div\\", _hoisted_1, [
_hoisted_2
]))
@@ -370,24 +365,24 @@ return function render() {
exports[`compiler: hoistStatic transform should hoist v-if props/children if static 1`] = `
"const _Vue = Vue
const { createVNode: _createVNode, createCommentVNode: _createCommentVNode } = Vue
const { createVNode: _createVNode, createCommentVNode: _createCommentVNode } = _Vue
const _hoisted_1 = {
key: 0,
id: \\"foo\\"
}
const _hoisted_2 = _createVNode(\\"span\\")
const _hoisted_2 = _createVNode(\\"span\\", null, null, -1 /* HOISTED */)
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
return function render() {
with (this) {
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
(_openBlock(), ok
? _createBlock(\\"div\\", _hoisted_1, [
ok
? (_openBlock(), _createBlock(\\"div\\", _hoisted_1, [
_hoisted_2
])
: _createCommentVNode(\\"v-if\\", true))
]))
: _createCommentVNode(\\"v-if\\", true)
]))
}
}"

View File

@@ -3,11 +3,11 @@
exports[`compiler: transform text <template v-for> 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createTextVNode: _createTextVNode } = _Vue
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(list, (i) => {
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createTextVNode: _createTextVNode } = _Vue
return (_openBlock(true), _createBlock(_Fragment, null, _renderList(list, (i) => {
return (_openBlock(), _createBlock(_Fragment, null, [
_createTextVNode(\\"foo\\")
], 64 /* STABLE_FRAGMENT */))
@@ -19,11 +19,11 @@ return function render() {
exports[`compiler: transform text consecutive text 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { toString: _toString } = _Vue
return _toString(foo) + \\" bar \\" + _toString(baz)
return function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString } = _Vue
return _toDisplayString(foo) + \\" bar \\" + _toDisplayString(baz)
}
}"
`;
@@ -31,13 +31,13 @@ return function render() {
exports[`compiler: transform text consecutive text between elements 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { createVNode: _createVNode, toString: _toString, createTextVNode: _createTextVNode, createBlock: _createBlock, Fragment: _Fragment, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(_Fragment, null, [
_createVNode(\\"div\\"),
_createTextVNode(_toString(foo) + \\" bar \\" + _toString(baz), 1 /* TEXT */),
_createTextVNode(_toDisplayString(foo) + \\" bar \\" + _toDisplayString(baz), 1 /* TEXT */),
_createVNode(\\"div\\")
], 64 /* STABLE_FRAGMENT */))
}
@@ -47,13 +47,13 @@ return function render() {
exports[`compiler: transform text consecutive text mixed with elements 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { createVNode: _createVNode, toString: _toString, createTextVNode: _createTextVNode, createBlock: _createBlock, Fragment: _Fragment, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(_Fragment, null, [
_createVNode(\\"div\\"),
_createTextVNode(_toString(foo) + \\" bar \\" + _toString(baz), 1 /* TEXT */),
_createTextVNode(_toDisplayString(foo) + \\" bar \\" + _toDisplayString(baz), 1 /* TEXT */),
_createVNode(\\"div\\"),
_createTextVNode(\\"hello\\"),
_createVNode(\\"div\\")
@@ -65,11 +65,11 @@ return function render() {
exports[`compiler: transform text no consecutive text 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { toString: _toString } = _Vue
return _toString(foo)
return function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString } = _Vue
return _toDisplayString(foo)
}
}"
`;
@@ -77,10 +77,10 @@ return function render() {
exports[`compiler: transform text text between elements (static) 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { createVNode: _createVNode, createTextVNode: _createTextVNode, createBlock: _createBlock, Fragment: _Fragment, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, createTextVNode: _createTextVNode, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(_Fragment, null, [
_createVNode(\\"div\\"),
_createTextVNode(\\"hello\\"),
@@ -91,10 +91,9 @@ return function render() {
`;
exports[`compiler: transform text with prefixIdentifiers: true 1`] = `
"const { toString } = Vue
"const { toDisplayString: _toDisplayString } = Vue
return function render() {
const _ctx = this
return toString(_ctx.foo) + \\" bar \\" + toString(_ctx.baz + _ctx.qux)
return function render(_ctx, _cache) {
return _toDisplayString(_ctx.foo) + \\" bar \\" + _toDisplayString(_ctx.baz + _ctx.qux)
}"
`;

View File

@@ -3,11 +3,11 @@
exports[`compiler: v-for codegen basic v-for 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (item) => {
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode } = _Vue
return (_openBlock(true), _createBlock(_Fragment, null, _renderList(items, (item) => {
return (_openBlock(), _createBlock(\\"span\\"))
}), 256 /* UNKEYED_FRAGMENT */))
}
@@ -17,11 +17,11 @@ return function render() {
exports[`compiler: v-for codegen keyed template v-for 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (item) => {
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode } = _Vue
return (_openBlock(true), _createBlock(_Fragment, null, _renderList(items, (item) => {
return (_openBlock(), _createBlock(_Fragment, { key: item }, [
\\"hello\\",
_createVNode(\\"span\\")
@@ -34,11 +34,11 @@ return function render() {
exports[`compiler: v-for codegen keyed v-for 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (item) => {
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(true), _createBlock(_Fragment, null, _renderList(items, (item) => {
return (_openBlock(), _createBlock(\\"span\\", { key: item }))
}), 128 /* KEYED_FRAGMENT */))
}
@@ -48,11 +48,11 @@ return function render() {
exports[`compiler: v-for codegen skipped key 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (item, __, index) => {
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode } = _Vue
return (_openBlock(true), _createBlock(_Fragment, null, _renderList(items, (item, __, index) => {
return (_openBlock(), _createBlock(\\"span\\"))
}), 256 /* UNKEYED_FRAGMENT */))
}
@@ -62,11 +62,11 @@ return function render() {
exports[`compiler: v-for codegen skipped value & key 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (_, __, index) => {
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode } = _Vue
return (_openBlock(true), _createBlock(_Fragment, null, _renderList(items, (_, __, index) => {
return (_openBlock(), _createBlock(\\"span\\"))
}), 256 /* UNKEYED_FRAGMENT */))
}
@@ -76,11 +76,11 @@ return function render() {
exports[`compiler: v-for codegen skipped value 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (_, key, index) => {
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode } = _Vue
return (_openBlock(true), _createBlock(_Fragment, null, _renderList(items, (_, key, index) => {
return (_openBlock(), _createBlock(\\"span\\"))
}), 256 /* UNKEYED_FRAGMENT */))
}
@@ -90,11 +90,11 @@ return function render() {
exports[`compiler: v-for codegen template v-for 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (item) => {
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode } = _Vue
return (_openBlock(true), _createBlock(_Fragment, null, _renderList(items, (item) => {
return (_openBlock(), _createBlock(_Fragment, null, [
\\"hello\\",
_createVNode(\\"span\\")
@@ -107,11 +107,11 @@ return function render() {
exports[`compiler: v-for codegen template v-for w/ <slot/> 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, renderSlot: _renderSlot } = _Vue
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (item) => {
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, renderSlot: _renderSlot } = _Vue
return (_openBlock(true), _createBlock(_Fragment, null, _renderList(items, (item) => {
return _renderSlot($slots, \\"default\\")
}), 256 /* UNKEYED_FRAGMENT */))
}
@@ -121,11 +121,11 @@ return function render() {
exports[`compiler: v-for codegen v-for on <slot/> 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, renderSlot: _renderSlot } = _Vue
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (item) => {
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, renderSlot: _renderSlot } = _Vue
return (_openBlock(true), _createBlock(_Fragment, null, _renderList(items, (item) => {
return _renderSlot($slots, \\"default\\")
}), 256 /* UNKEYED_FRAGMENT */))
}
@@ -135,16 +135,16 @@ return function render() {
exports[`compiler: v-for codegen v-for on element with custom directive 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode, withDirectives: _withDirectives, resolveDirective: _resolveDirective } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, resolveDirective: _resolveDirective, createVNode: _createVNode, withDirectives: _withDirectives } = _Vue
const _directive_foo = _resolveDirective(\\"foo\\")
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(list, (i) => {
return (_openBlock(), _withDirectives(_createBlock(\\"div\\", null, null, 32 /* NEED_PATCH */), [
return (_openBlock(true), _createBlock(_Fragment, null, _renderList(list, (i) => {
return _withDirectives((_openBlock(), _createBlock(\\"div\\", null, null, 512 /* NEED_PATCH */)), [
[_directive_foo]
]))
])
}), 256 /* UNKEYED_FRAGMENT */))
}
}"
@@ -153,15 +153,15 @@ return function render() {
exports[`compiler: v-for codegen v-if + v-for 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { openBlock: _openBlock, renderList: _renderList, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode, createCommentVNode: _createCommentVNode } = _Vue
return (_openBlock(), ok
? _createBlock(_Fragment, { key: 0 }, _renderList(list, (i) => {
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode, createCommentVNode: _createCommentVNode } = _Vue
return ok
? (_openBlock(true), _createBlock(_Fragment, { key: 0 }, _renderList(list, (i) => {
return (_openBlock(), _createBlock(\\"div\\"))
}), 256 /* UNKEYED_FRAGMENT */)
: _createCommentVNode(\\"v-if\\", true))
}), 256 /* UNKEYED_FRAGMENT */))
: _createCommentVNode(\\"v-if\\", true)
}
}"
`;
@@ -169,11 +169,11 @@ return function render() {
exports[`compiler: v-for codegen value + key + index 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, createVNode: _createVNode } = _Vue
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (item, key, index) => {
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode } = _Vue
return (_openBlock(true), _createBlock(_Fragment, null, _renderList(items, (item, key, index) => {
return (_openBlock(), _createBlock(\\"span\\"))
}), 256 /* UNKEYED_FRAGMENT */))
}

View File

@@ -3,13 +3,13 @@
exports[`compiler: v-if codegen basic v-if 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
return (_openBlock(), ok
? _createBlock(\\"div\\", { key: 0 })
: _createCommentVNode(\\"v-if\\", true))
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
return ok
? (_openBlock(), _createBlock(\\"div\\", { key: 0 }))
: _createCommentVNode(\\"v-if\\", true)
}
}"
`;
@@ -17,17 +17,17 @@ return function render() {
exports[`compiler: v-if codegen template v-if 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { openBlock: _openBlock, createVNode: _createVNode, Fragment: _Fragment, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
return (_openBlock(), ok
? _createBlock(_Fragment, { key: 0 }, [
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
return ok
? (_openBlock(), _createBlock(_Fragment, { key: 0 }, [
_createVNode(\\"div\\"),
\\"hello\\",
_createVNode(\\"p\\")
])
: _createCommentVNode(\\"v-if\\", true))
], 64 /* STABLE_FRAGMENT */))
: _createCommentVNode(\\"v-if\\", true)
}
}"
`;
@@ -35,13 +35,13 @@ return function render() {
exports[`compiler: v-if codegen template v-if w/ single <slot/> child 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { openBlock: _openBlock, renderSlot: _renderSlot, createCommentVNode: _createCommentVNode } = _Vue
return (_openBlock(), ok
return function render(_ctx, _cache) {
with (_ctx) {
const { renderSlot: _renderSlot, createCommentVNode: _createCommentVNode } = _Vue
return ok
? _renderSlot($slots, \\"default\\", { key: 0 })
: _createCommentVNode(\\"v-if\\", true))
: _createCommentVNode(\\"v-if\\", true)
}
}"
`;
@@ -49,13 +49,13 @@ return function render() {
exports[`compiler: v-if codegen v-if + v-else 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
return (_openBlock(), ok
? _createBlock(\\"div\\", { key: 0 })
: _createBlock(\\"p\\", { key: 1 }))
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
return ok
? (_openBlock(), _createBlock(\\"div\\", { key: 0 }))
: (_openBlock(), _createBlock(\\"p\\", { key: 1 }))
}
}"
`;
@@ -63,15 +63,15 @@ return function render() {
exports[`compiler: 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, createCommentVNode: _createCommentVNode, Fragment: _Fragment } = _Vue
return (_openBlock(), ok
? _createBlock(\\"div\\", { key: 0 })
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode, Fragment: _Fragment } = _Vue
return ok
? (_openBlock(), _createBlock(\\"div\\", { key: 0 }))
: orNot
? _createBlock(\\"p\\", { key: 1 })
: _createBlock(_Fragment, { key: 2 }, [\\"fine\\"]))
? (_openBlock(), _createBlock(\\"p\\", { key: 1 }))
: (_openBlock(), _createBlock(_Fragment, { key: 2 }, [\\"fine\\"], 64 /* STABLE_FRAGMENT */))
}
}"
`;
@@ -79,15 +79,15 @@ return function render() {
exports[`compiler: v-if codegen v-if + v-else-if 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
return (_openBlock(), ok
? _createBlock(\\"div\\", { key: 0 })
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
return ok
? (_openBlock(), _createBlock(\\"div\\", { key: 0 }))
: orNot
? _createBlock(\\"p\\", { key: 1 })
: _createCommentVNode(\\"v-if\\", true))
? (_openBlock(), _createBlock(\\"p\\", { key: 1 }))
: _createCommentVNode(\\"v-if\\", true)
}
}"
`;
@@ -95,13 +95,27 @@ return function render() {
exports[`compiler: v-if codegen v-if on <slot/> 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { openBlock: _openBlock, renderSlot: _renderSlot, createCommentVNode: _createCommentVNode } = _Vue
return (_openBlock(), ok
return function render(_ctx, _cache) {
with (_ctx) {
const { renderSlot: _renderSlot, createCommentVNode: _createCommentVNode } = _Vue
return ok
? _renderSlot($slots, \\"default\\", { key: 0 })
: _createCommentVNode(\\"v-if\\", true))
: _createCommentVNode(\\"v-if\\", true)
}
}"
`;
exports[`compiler: v-if codegen v-if with key 1`] = `
"const _Vue = Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue
return ok
? (_openBlock(), _createBlock(\\"div\\", { key: \\"some-key\\" }))
: _createCommentVNode(\\"v-if\\", true)
}
}"
`;

View File

@@ -1,11 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`compiler: transform v-model compound expression (with prefixIdentifiers) 1`] = `
"import { createVNode, createBlock, openBlock } from \\"vue\\"
"import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
export default function render() {
const _ctx = this
return (openBlock(), createBlock(\\"input\\", {
export function render(_ctx, _cache) {
return (_openBlock(), _createBlock(\\"input\\", {
modelValue: _ctx.model[_ctx.index],
\\"onUpdate:modelValue\\": $event => (_ctx.model[_ctx.index] = $event)
}, null, 8 /* PROPS */, [\\"modelValue\\", \\"onUpdate:modelValue\\"]))
@@ -15,10 +14,10 @@ export default function render() {
exports[`compiler: transform v-model compound expression 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(\\"input\\", {
modelValue: model[index],
\\"onUpdate:modelValue\\": $event => (model[index] = $event)
@@ -28,11 +27,10 @@ return function render() {
`;
exports[`compiler: transform v-model simple exprssion (with prefixIdentifiers) 1`] = `
"import { createVNode, createBlock, openBlock } from \\"vue\\"
"import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
export default function render() {
const _ctx = this
return (openBlock(), createBlock(\\"input\\", {
export function render(_ctx, _cache) {
return (_openBlock(), _createBlock(\\"input\\", {
modelValue: _ctx.model,
\\"onUpdate:modelValue\\": $event => (_ctx.model = $event)
}, null, 8 /* PROPS */, [\\"modelValue\\", \\"onUpdate:modelValue\\"]))
@@ -42,10 +40,10 @@ export default function render() {
exports[`compiler: transform v-model simple exprssion 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(\\"input\\", {
modelValue: model,
\\"onUpdate:modelValue\\": $event => (model = $event)
@@ -57,24 +55,23 @@ return function render() {
exports[`compiler: transform v-model with argument 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(\\"input\\", {
value: model,
\\"onUpdate:value\\": $event => (model = $event)
}, null, 8 /* PROPS */, [\\"value\\", \\"onUpdate:value\\"]))
}, null, 40 /* PROPS, HYDRATE_EVENTS */, [\\"value\\", \\"onUpdate:value\\"]))
}
}"
`;
exports[`compiler: transform v-model with dynamic argument (with prefixIdentifiers) 1`] = `
"import { createVNode, createBlock, openBlock } from \\"vue\\"
"import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
export default function render() {
const _ctx = this
return (openBlock(), createBlock(\\"input\\", {
export function render(_ctx, _cache) {
return (_openBlock(), _createBlock(\\"input\\", {
[_ctx.value]: _ctx.model,
[\\"onUpdate:\\" + _ctx.value]: $event => (_ctx.model = $event)
}, null, 16 /* FULL_PROPS */))
@@ -84,10 +81,10 @@ export default function render() {
exports[`compiler: transform v-model with dynamic argument 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(\\"input\\", {
[value]: model,
[\\"onUpdate:\\" + value]: $event => (model = $event)

View File

@@ -3,11 +3,10 @@
exports[`compiler: v-once transform as root node 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
return function render(_ctx, _cache) {
with (_ctx) {
const { setBlockTracking: _setBlockTracking, createVNode: _createVNode } = _Vue
const _cache = $cache
return _cache[1] || (
_setBlockTracking(-1),
_cache[1] = _createVNode(\\"div\\", { id: foo }, null, 8 /* PROPS */, [\\"id\\"]),
@@ -21,13 +20,12 @@ return function render() {
exports[`compiler: v-once transform on component 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { setBlockTracking: _setBlockTracking, resolveComponent: _resolveComponent, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
const _cache = $cache
return function render(_ctx, _cache) {
with (_ctx) {
const { setBlockTracking: _setBlockTracking, resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(\\"div\\", null, [
_cache[1] || (
_setBlockTracking(-1),
@@ -43,11 +41,10 @@ return function render() {
exports[`compiler: v-once transform on nested plain element 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { setBlockTracking: _setBlockTracking, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
const _cache = $cache
return function render(_ctx, _cache) {
with (_ctx) {
const { setBlockTracking: _setBlockTracking, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
_cache[1] || (
_setBlockTracking(-1),
@@ -63,11 +60,10 @@ return function render() {
exports[`compiler: v-once transform on slot outlet 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { setBlockTracking: _setBlockTracking, renderSlot: _renderSlot, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
const _cache = $cache
return function render(_ctx, _cache) {
with (_ctx) {
const { setBlockTracking: _setBlockTracking, renderSlot: _renderSlot, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
_cache[1] || (
_setBlockTracking(-1),
@@ -83,11 +79,10 @@ return function render() {
exports[`compiler: v-once transform with hoistStatic: true 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { setBlockTracking: _setBlockTracking, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
const _cache = $cache
return function render(_ctx, _cache) {
with (_ctx) {
const { setBlockTracking: _setBlockTracking, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
_cache[1] || (
_setBlockTracking(-1),

View File

@@ -1,111 +1,93 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`compiler: transform component slots dynamically named slots 1`] = `
"const { toString, resolveComponent, createVNode, createBlock, openBlock } = Vue
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
return function render() {
const _ctx = this
const _component_Comp = resolveComponent(\\"Comp\\")
return (openBlock(), createBlock(_component_Comp, null, {
[_ctx.one]: ({ foo }) => [toString(foo), toString(_ctx.bar)],
[_ctx.two]: ({ bar }) => [toString(_ctx.foo), toString(bar)],
_compiled: true
}, 512 /* DYNAMIC_SLOTS */))
}"
`;
return function render(_ctx, _cache) {
const _component_Comp = _resolveComponent(\\"Comp\\")
exports[`compiler: transform component slots explicit default slot 1`] = `
"const { toString, resolveComponent, createVNode, createBlock, openBlock } = Vue
return function render() {
const _ctx = this
const _component_Comp = resolveComponent(\\"Comp\\")
return (openBlock(), createBlock(_component_Comp, null, {
default: ({ foo }) => [toString(foo), toString(_ctx.bar)],
_compiled: true
}))
return (_openBlock(), _createBlock(_component_Comp, null, {
[_ctx.one]: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)]),
[_ctx.two]: _withCtx(({ bar }) => [_toDisplayString(_ctx.foo), _toDisplayString(bar)]),
_: 1
}, 1024 /* DYNAMIC_SLOTS */))
}"
`;
exports[`compiler: transform component slots implicit default slot 1`] = `
"const { createVNode, resolveComponent, createBlock, openBlock } = Vue
"const { createVNode: _createVNode, resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue
return function render() {
const _ctx = this
const _component_Comp = resolveComponent(\\"Comp\\")
return (openBlock(), createBlock(_component_Comp, null, {
default: () => [
createVNode(\\"div\\")
],
_compiled: true
return function render(_ctx, _cache) {
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(_component_Comp, null, {
default: _withCtx(() => [
_createVNode(\\"div\\")
]),
_: 1
}))
}"
`;
exports[`compiler: transform component slots named slot with v-for w/ prefixIdentifiers: true 1`] = `
"const { toString, resolveComponent, renderList, createSlots, createVNode, createBlock, openBlock } = Vue
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, renderList: _renderList, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
return function render() {
const _ctx = this
const _component_Comp = resolveComponent(\\"Comp\\")
return (openBlock(), createBlock(_component_Comp, null, createSlots({ _compiled: true }, [
renderList(_ctx.list, (name) => {
return function render(_ctx, _cache) {
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 1 }, [
_renderList(_ctx.list, (name) => {
return {
name: name,
fn: () => [toString(name)]
fn: _withCtx(() => [_toDisplayString(name)])
}
})
]), 512 /* DYNAMIC_SLOTS */))
]), 1024 /* DYNAMIC_SLOTS */))
}"
`;
exports[`compiler: transform component slots named slot with v-if + prefixIdentifiers: true 1`] = `
"const { toString, resolveComponent, createSlots, createVNode, createBlock, openBlock } = Vue
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
return function render() {
const _ctx = this
const _component_Comp = resolveComponent(\\"Comp\\")
return (openBlock(), createBlock(_component_Comp, null, createSlots({ _compiled: true }, [
return function render(_ctx, _cache) {
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 1 }, [
(_ctx.ok)
? {
name: \\"one\\",
fn: (props) => [toString(props)]
fn: _withCtx((props) => [_toDisplayString(props)])
}
: undefined
]), 512 /* DYNAMIC_SLOTS */))
]), 1024 /* DYNAMIC_SLOTS */))
}"
`;
exports[`compiler: transform component slots named slot with v-if + v-else-if + v-else 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { resolveComponent: _resolveComponent, createSlots: _createSlots, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { resolveComponent: _resolveComponent, withCtx: _withCtx, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _compiled: true }, [
return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 1 }, [
ok
? {
name: \\"one\\",
fn: () => [\\"foo\\"]
fn: _withCtx(() => [\\"foo\\"])
}
: orNot
? {
name: \\"two\\",
fn: (props) => [\\"bar\\"]
fn: _withCtx((props) => [\\"bar\\"])
}
: {
name: \\"one\\",
fn: () => [\\"baz\\"]
fn: _withCtx(() => [\\"baz\\"])
}
]), 512 /* DYNAMIC_SLOTS */))
]), 1024 /* DYNAMIC_SLOTS */))
}
}"
`;
@@ -113,59 +95,117 @@ return function render() {
exports[`compiler: transform component slots named slot with v-if 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { resolveComponent: _resolveComponent, createSlots: _createSlots, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { resolveComponent: _resolveComponent, withCtx: _withCtx, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _compiled: true }, [
return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 1 }, [
ok
? {
name: \\"one\\",
fn: () => [\\"hello\\"]
fn: _withCtx(() => [\\"hello\\"])
}
: undefined
]), 512 /* DYNAMIC_SLOTS */))
]), 1024 /* DYNAMIC_SLOTS */))
}
}"
`;
exports[`compiler: transform component slots named slots 1`] = `
"const { toString, resolveComponent, createVNode, createBlock, openBlock } = Vue
exports[`compiler: transform component slots named slots w/ implicit default slot 1`] = `
"const _Vue = Vue
return function render() {
const _ctx = this
const _component_Comp = resolveComponent(\\"Comp\\")
return (openBlock(), createBlock(_component_Comp, null, {
one: ({ foo }) => [toString(foo), toString(_ctx.bar)],
two: ({ bar }) => [toString(_ctx.foo), toString(bar)],
_compiled: true
}))
return function render(_ctx, _cache) {
with (_ctx) {
const { createVNode: _createVNode, resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = _Vue
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(_component_Comp, null, {
one: _withCtx(() => [\\"foo\\"]),
default: _withCtx(() => [
\\"bar\\",
_createVNode(\\"span\\")
]),
_: 1
}))
}
}"
`;
exports[`compiler: transform component slots nested slots scoping 1`] = `
"const { toString, resolveComponent, createVNode, createBlock, openBlock } = Vue
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
return function render() {
const _ctx = this
const _component_Inner = resolveComponent(\\"Inner\\")
const _component_Comp = resolveComponent(\\"Comp\\")
return (openBlock(), createBlock(_component_Comp, null, {
default: ({ foo }) => [
createVNode(_component_Inner, null, {
default: ({ bar }) => [toString(foo), toString(bar), toString(_ctx.baz)],
_compiled: true
}, 512 /* DYNAMIC_SLOTS */),
return function render(_ctx, _cache) {
const _component_Inner = _resolveComponent(\\"Inner\\")
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(_component_Comp, null, {
default: _withCtx(({ foo }) => [
_createVNode(_component_Inner, null, {
default: _withCtx(({ bar }) => [_toDisplayString(foo), _toDisplayString(bar), _toDisplayString(_ctx.baz)]),
_: 1
}, 1024 /* DYNAMIC_SLOTS */),
\\" \\",
toString(foo),
toString(_ctx.bar),
toString(_ctx.baz)
],
_compiled: true
_toDisplayString(foo),
_toDisplayString(_ctx.bar),
_toDisplayString(_ctx.baz)
]),
_: 1
}))
}"
`;
exports[`compiler: transform component slots on component dynamically named slot 1`] = `
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
return function render(_ctx, _cache) {
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(_component_Comp, null, {
[_ctx.named]: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)]),
_: 1
}))
}"
`;
exports[`compiler: transform component slots on component named slot 1`] = `
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
return function render(_ctx, _cache) {
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(_component_Comp, null, {
named: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)]),
_: 1
}))
}"
`;
exports[`compiler: transform component slots on-component default slot 1`] = `
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
return function render(_ctx, _cache) {
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(_component_Comp, null, {
default: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)]),
_: 1
}))
}"
`;
exports[`compiler: transform component slots template named slots 1`] = `
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
return function render(_ctx, _cache) {
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(_component_Comp, null, {
one: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)]),
two: _withCtx(({ bar }) => [_toDisplayString(_ctx.foo), _toDisplayString(bar)]),
_: 1
}))
}"
`;

View File

@@ -1,18 +1,15 @@
import {
parse,
baseParse as parse,
transform,
NodeTypes,
generate,
CompilerOptions
CompilerOptions,
VNodeCall,
IfNode,
ElementNode,
ForNode
} from '../../src'
import {
OPEN_BLOCK,
CREATE_BLOCK,
CREATE_VNODE,
WITH_DIRECTIVES,
FRAGMENT,
RENDER_LIST
} from '../../src/runtimeHelpers'
import { FRAGMENT, RENDER_LIST, CREATE_TEXT } from '../../src/runtimeHelpers'
import { transformElement } from '../../src/transforms/transformElement'
import { transformExpression } from '../../src/transforms/transformExpression'
import { transformIf } from '../../src/transforms/vIf'
@@ -20,6 +17,7 @@ import { transformFor } from '../../src/transforms/vFor'
import { transformBind } from '../../src/transforms/vBind'
import { transformOn } from '../../src/transforms/vOn'
import { createObjectMatcher, genFlagText } from '../testUtils'
import { transformText } from '../../src/transforms/transformText'
import { PatchFlags } from '@vue/shared'
function transformWithHoist(template: string, options: CompilerOptions = {}) {
@@ -30,7 +28,8 @@ function transformWithHoist(template: string, options: CompilerOptions = {}) {
transformIf,
transformFor,
...(options.prefixIdentifiers ? [transformExpression] : []),
transformElement
transformElement,
transformText
],
directiveTransforms: {
on: transformOn,
@@ -39,56 +38,43 @@ function transformWithHoist(template: string, options: CompilerOptions = {}) {
...options
})
expect(ast.codegenNode).toMatchObject({
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
expressions: [
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: OPEN_BLOCK
},
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_BLOCK
}
]
type: NodeTypes.VNODE_CALL,
isBlock: true
})
return {
root: ast,
args: (ast.codegenNode as any).expressions[1].arguments
}
return ast
}
describe('compiler: hoistStatic transform', () => {
test('should NOT hoist root node', () => {
// if the whole tree is static, the root still needs to be a block
// so that it's patched in optimized mode to skip children
const { root, args } = transformWithHoist(`<div/>`)
const root = transformWithHoist(`<div/>`)
expect(root.hoists.length).toBe(0)
expect(args).toEqual([`"div"`])
expect(root.codegenNode).toMatchObject({
tag: `"div"`
})
expect(generate(root).code).toMatchSnapshot()
})
test('hoist simple element', () => {
const { root, args } = transformWithHoist(
const root = transformWithHoist(
`<div><span class="inline">hello</span></div>`
)
expect(root.hoists).toMatchObject([
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_VNODE,
arguments: [
`"span"`,
createObjectMatcher({ class: 'inline' }),
{
type: NodeTypes.TEXT,
content: `hello`
}
]
type: NodeTypes.VNODE_CALL,
tag: `"span"`,
props: createObjectMatcher({ class: 'inline' }),
children: {
type: NodeTypes.TEXT,
content: `hello`
}
}
])
expect(args).toMatchObject([
`"div"`,
`null`,
[
expect(root.codegenNode).toMatchObject({
tag: `"div"`,
props: undefined,
children: [
{
type: NodeTypes.ELEMENT,
codegenNode: {
@@ -97,29 +83,24 @@ describe('compiler: hoistStatic transform', () => {
}
}
]
])
})
expect(generate(root).code).toMatchSnapshot()
})
test('hoist nested static tree', () => {
const { root, args } = transformWithHoist(
`<div><p><span/><span/></p></div>`
)
const root = transformWithHoist(`<div><p><span/><span/></p></div>`)
expect(root.hoists).toMatchObject([
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_VNODE,
arguments: [
`"p"`,
`null`,
[
{ type: NodeTypes.ELEMENT, tag: `span` },
{ type: NodeTypes.ELEMENT, tag: `span` }
]
type: NodeTypes.VNODE_CALL,
tag: `"p"`,
props: undefined,
children: [
{ type: NodeTypes.ELEMENT, tag: `span` },
{ type: NodeTypes.ELEMENT, tag: `span` }
]
}
])
expect(args[2]).toMatchObject([
expect((root.codegenNode as VNodeCall).children).toMatchObject([
{
type: NodeTypes.ELEMENT,
codegenNode: {
@@ -132,21 +113,16 @@ describe('compiler: hoistStatic transform', () => {
})
test('hoist nested static tree with comments', () => {
const { root, args } = transformWithHoist(
`<div><div><!--comment--></div></div>`
)
const root = transformWithHoist(`<div><div><!--comment--></div></div>`)
expect(root.hoists).toMatchObject([
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_VNODE,
arguments: [
`"div"`,
`null`,
[{ type: NodeTypes.COMMENT, content: `comment` }]
]
type: NodeTypes.VNODE_CALL,
tag: `"div"`,
props: undefined,
children: [{ type: NodeTypes.COMMENT, content: `comment` }]
}
])
expect(args[2]).toMatchObject([
expect((root.codegenNode as VNodeCall).children).toMatchObject([
{
type: NodeTypes.ELEMENT,
codegenNode: {
@@ -159,20 +135,18 @@ describe('compiler: hoistStatic transform', () => {
})
test('hoist siblings with common non-hoistable parent', () => {
const { root, args } = transformWithHoist(`<div><span/><div/></div>`)
const root = transformWithHoist(`<div><span/><div/></div>`)
expect(root.hoists).toMatchObject([
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_VNODE,
arguments: [`"span"`]
type: NodeTypes.VNODE_CALL,
tag: `"span"`
},
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_VNODE,
arguments: [`"div"`]
type: NodeTypes.VNODE_CALL,
tag: `"div"`
}
])
expect(args[2]).toMatchObject([
expect((root.codegenNode as VNodeCall).children).toMatchObject([
{
type: NodeTypes.ELEMENT,
codegenNode: {
@@ -192,14 +166,14 @@ describe('compiler: hoistStatic transform', () => {
})
test('should NOT hoist components', () => {
const { root, args } = transformWithHoist(`<div><Comp/></div>`)
const root = transformWithHoist(`<div><Comp/></div>`)
expect(root.hoists.length).toBe(0)
expect(args[2]).toMatchObject([
expect((root.codegenNode as VNodeCall).children).toMatchObject([
{
type: NodeTypes.ELEMENT,
codegenNode: {
callee: CREATE_VNODE,
arguments: [`_component_Comp`]
type: NodeTypes.VNODE_CALL,
tag: `_component_Comp`
}
}
])
@@ -207,22 +181,20 @@ describe('compiler: hoistStatic transform', () => {
})
test('should NOT hoist element with dynamic props', () => {
const { root, args } = transformWithHoist(`<div><div :id="foo"/></div>`)
const root = transformWithHoist(`<div><div :id="foo"/></div>`)
expect(root.hoists.length).toBe(0)
expect(args[2]).toMatchObject([
expect((root.codegenNode as VNodeCall).children).toMatchObject([
{
type: NodeTypes.ELEMENT,
codegenNode: {
callee: CREATE_VNODE,
arguments: [
`"div"`,
createObjectMatcher({
id: `[foo]`
}),
`null`,
genFlagText(PatchFlags.PROPS),
`["id"]`
]
type: NodeTypes.VNODE_CALL,
tag: `"div"`,
props: createObjectMatcher({
id: `[foo]`
}),
children: undefined,
patchFlag: genFlagText(PatchFlags.PROPS),
dynamicProps: `["id"]`
}
}
])
@@ -230,19 +202,19 @@ describe('compiler: hoistStatic transform', () => {
})
test('hoist element with static key', () => {
const { root, args } = transformWithHoist(`<div><div key="foo"/></div>`)
const root = transformWithHoist(`<div><div key="foo"/></div>`)
expect(root.hoists.length).toBe(1)
expect(root.hoists).toMatchObject([
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_VNODE,
arguments: [`"div"`, createObjectMatcher({ key: 'foo' })]
type: NodeTypes.VNODE_CALL,
tag: `"div"`,
props: createObjectMatcher({ key: 'foo' })
}
])
expect(args).toMatchObject([
`"div"`,
`null`,
[
expect(root.codegenNode).toMatchObject({
tag: `"div"`,
props: undefined,
children: [
{
type: NodeTypes.ELEMENT,
codegenNode: {
@@ -251,24 +223,22 @@ describe('compiler: hoistStatic transform', () => {
}
}
]
])
})
expect(generate(root).code).toMatchSnapshot()
})
test('should NOT hoist element with dynamic key', () => {
const { root, args } = transformWithHoist(`<div><div :key="foo"/></div>`)
const root = transformWithHoist(`<div><div :key="foo"/></div>`)
expect(root.hoists.length).toBe(0)
expect(args[2]).toMatchObject([
expect((root.codegenNode as VNodeCall).children).toMatchObject([
{
type: NodeTypes.ELEMENT,
codegenNode: {
callee: CREATE_VNODE,
arguments: [
`"div"`,
createObjectMatcher({
key: `[foo]`
})
]
type: NodeTypes.VNODE_CALL,
tag: `"div"`,
props: createObjectMatcher({
key: `[foo]`
})
}
}
])
@@ -276,21 +246,19 @@ describe('compiler: hoistStatic transform', () => {
})
test('should NOT hoist element with dynamic ref', () => {
const { root, args } = transformWithHoist(`<div><div :ref="foo"/></div>`)
const root = transformWithHoist(`<div><div :ref="foo"/></div>`)
expect(root.hoists.length).toBe(0)
expect(args[2]).toMatchObject([
expect((root.codegenNode as VNodeCall).children).toMatchObject([
{
type: NodeTypes.ELEMENT,
codegenNode: {
callee: CREATE_VNODE,
arguments: [
`"div"`,
createObjectMatcher({
ref: `[foo]`
}),
`null`,
genFlagText(PatchFlags.NEED_PATCH)
]
type: NodeTypes.VNODE_CALL,
tag: `"div"`,
props: createObjectMatcher({
ref: `[foo]`
}),
children: undefined,
patchFlag: genFlagText(PatchFlags.NEED_PATCH)
}
}
])
@@ -298,32 +266,23 @@ describe('compiler: hoistStatic transform', () => {
})
test('hoist static props for elements with directives', () => {
const { root, args } = transformWithHoist(
`<div><div id="foo" v-foo/></div>`
)
const root = transformWithHoist(`<div><div id="foo" v-foo/></div>`)
expect(root.hoists).toMatchObject([createObjectMatcher({ id: 'foo' })])
expect(args[2]).toMatchObject([
expect((root.codegenNode as VNodeCall).children).toMatchObject([
{
type: NodeTypes.ELEMENT,
codegenNode: {
callee: WITH_DIRECTIVES,
arguments: [
{
callee: CREATE_VNODE,
arguments: [
`"div"`,
{
type: NodeTypes.SIMPLE_EXPRESSION,
content: `_hoisted_1`
},
`null`,
genFlagText(PatchFlags.NEED_PATCH)
]
},
{
type: NodeTypes.JS_ARRAY_EXPRESSION
}
]
type: NodeTypes.VNODE_CALL,
tag: `"div"`,
props: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: `_hoisted_1`
},
children: undefined,
patchFlag: genFlagText(PatchFlags.NEED_PATCH),
directives: {
type: NodeTypes.JS_ARRAY_EXPRESSION
}
}
}
])
@@ -331,21 +290,19 @@ describe('compiler: hoistStatic transform', () => {
})
test('hoist static props for elements with dynamic text children', () => {
const { root, args } = transformWithHoist(
const root = transformWithHoist(
`<div><div id="foo">{{ hello }}</div></div>`
)
expect(root.hoists).toMatchObject([createObjectMatcher({ id: 'foo' })])
expect(args[2]).toMatchObject([
expect((root.codegenNode as VNodeCall).children).toMatchObject([
{
type: NodeTypes.ELEMENT,
codegenNode: {
callee: CREATE_VNODE,
arguments: [
`"div"`,
{ content: `_hoisted_1` },
{ type: NodeTypes.INTERPOLATION },
genFlagText(PatchFlags.TEXT)
]
type: NodeTypes.VNODE_CALL,
tag: `"div"`,
props: { content: `_hoisted_1` },
children: { type: NodeTypes.INTERPOLATION },
patchFlag: genFlagText(PatchFlags.TEXT)
}
}
])
@@ -353,20 +310,16 @@ describe('compiler: hoistStatic transform', () => {
})
test('hoist static props for elements with unhoistable children', () => {
const { root, args } = transformWithHoist(
`<div><div id="foo"><Comp/></div></div>`
)
const root = transformWithHoist(`<div><div id="foo"><Comp/></div></div>`)
expect(root.hoists).toMatchObject([createObjectMatcher({ id: 'foo' })])
expect(args[2]).toMatchObject([
expect((root.codegenNode as VNodeCall).children).toMatchObject([
{
type: NodeTypes.ELEMENT,
codegenNode: {
callee: CREATE_VNODE,
arguments: [
`"div"`,
{ content: `_hoisted_1` },
[{ type: NodeTypes.ELEMENT, tag: `Comp` }]
]
type: NodeTypes.VNODE_CALL,
tag: `"div"`,
props: { content: `_hoisted_1` },
children: [{ type: NodeTypes.ELEMENT, tag: `Comp` }]
}
}
])
@@ -374,7 +327,7 @@ describe('compiler: hoistStatic transform', () => {
})
test('should hoist v-if props/children if static', () => {
const { root, args } = transformWithHoist(
const root = transformWithHoist(
`<div><div v-if="ok" id="foo"><span/></div></div>`
)
expect(root.hoists).toMatchObject([
@@ -383,37 +336,31 @@ describe('compiler: hoistStatic transform', () => {
id: 'foo'
}),
{
callee: CREATE_VNODE,
arguments: [`"span"`]
type: NodeTypes.VNODE_CALL,
tag: `"span"`
}
])
expect(args[2][0].codegenNode).toMatchObject({
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
expressions: [
{ callee: OPEN_BLOCK },
{
type: NodeTypes.JS_CONDITIONAL_EXPRESSION,
consequent: {
// blocks should NOT be hoisted
callee: CREATE_BLOCK,
arguments: [
`"div"`,
{ content: `_hoisted_1` },
[
{
codegenNode: { content: `_hoisted_2` }
}
]
]
expect(
((root.children[0] as ElementNode).children[0] as IfNode).codegenNode
).toMatchObject({
type: NodeTypes.JS_CONDITIONAL_EXPRESSION,
consequent: {
// blocks should NOT be hoisted
type: NodeTypes.VNODE_CALL,
tag: `"div"`,
props: { content: `_hoisted_1` },
children: [
{
codegenNode: { content: `_hoisted_2` }
}
}
]
]
}
})
expect(generate(root).code).toMatchSnapshot()
})
test('should hoist v-for children if static', () => {
const { root, args } = transformWithHoist(
const root = transformWithHoist(
`<div><div v-for="i in list" id="foo"><span/></div></div>`
)
expect(root.hoists).toMatchObject([
@@ -421,55 +368,58 @@ describe('compiler: hoistStatic transform', () => {
id: 'foo'
}),
{
callee: CREATE_VNODE,
arguments: [`"span"`]
type: NodeTypes.VNODE_CALL,
tag: `"span"`
}
])
const forBlockCodegen = args[2][0].codegenNode
const forBlockCodegen = ((root.children[0] as ElementNode)
.children[0] as ForNode).codegenNode
expect(forBlockCodegen).toMatchObject({
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
expressions: [
{ callee: OPEN_BLOCK },
{
callee: CREATE_BLOCK,
arguments: [
FRAGMENT,
`null`,
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: RENDER_LIST
},
genFlagText(PatchFlags.UNKEYED_FRAGMENT)
]
}
]
type: NodeTypes.VNODE_CALL,
tag: FRAGMENT,
props: undefined,
children: {
type: NodeTypes.JS_CALL_EXPRESSION,
callee: RENDER_LIST
},
patchFlag: genFlagText(PatchFlags.UNKEYED_FRAGMENT)
})
const innerBlockCodegen =
forBlockCodegen.expressions[1].arguments[2].arguments[1].returns
expect(innerBlockCodegen).toMatchObject({
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
expressions: [
{ callee: OPEN_BLOCK },
const innerBlockCodegen = forBlockCodegen!.children.arguments[1]
expect(innerBlockCodegen.returns).toMatchObject({
type: NodeTypes.VNODE_CALL,
tag: `"div"`,
props: { content: `_hoisted_1` },
children: [
{
callee: CREATE_BLOCK,
arguments: [
`"div"`,
{ content: `_hoisted_1` },
[
{
codegenNode: { content: `_hoisted_2` }
}
]
]
codegenNode: { content: `_hoisted_2` }
}
]
})
expect(generate(root).code).toMatchSnapshot()
})
test('hoist static text node between elements', () => {
const root = transformWithHoist(`<div>static<div>static</div></div>`)
expect(root.hoists).toMatchObject([
{
callee: CREATE_TEXT,
arguments: [
{
type: NodeTypes.TEXT,
content: `static`
}
]
},
{
type: NodeTypes.VNODE_CALL,
tag: `"div"`
}
])
})
describe('prefixIdentifiers', () => {
test('hoist nested static tree with static interpolation', () => {
const { root, args } = transformWithHoist(
const root = transformWithHoist(
`<div><span>foo {{ 1 }} {{ true }}</span></div>`,
{
prefixIdentifiers: true
@@ -477,44 +427,18 @@ describe('compiler: hoistStatic transform', () => {
)
expect(root.hoists).toMatchObject([
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_VNODE,
arguments: [
`"span"`,
`null`,
[
{
type: NodeTypes.TEXT,
content: `foo `
},
{
type: NodeTypes.INTERPOLATION,
content: {
content: `1`,
isStatic: false,
isConstant: true
}
},
{
type: NodeTypes.TEXT,
content: ` `
},
{
type: NodeTypes.INTERPOLATION,
content: {
content: `true`,
isStatic: false,
isConstant: true
}
}
]
]
type: NodeTypes.VNODE_CALL,
tag: `"span"`,
props: undefined,
children: {
type: NodeTypes.COMPOUND_EXPRESSION
}
}
])
expect(args).toMatchObject([
`"div"`,
`null`,
[
expect(root.codegenNode).toMatchObject({
tag: `"div"`,
props: undefined,
children: [
{
type: NodeTypes.ELEMENT,
codegenNode: {
@@ -523,12 +447,12 @@ describe('compiler: hoistStatic transform', () => {
}
}
]
])
})
expect(generate(root).code).toMatchSnapshot()
})
test('hoist nested static tree with static prop value', () => {
const { root, args } = transformWithHoist(
const root = transformWithHoist(
`<div><span :foo="0">{{ 1 }}</span></div>`,
{
prefixIdentifiers: true
@@ -537,26 +461,23 @@ describe('compiler: hoistStatic transform', () => {
expect(root.hoists).toMatchObject([
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_VNODE,
arguments: [
`"span"`,
createObjectMatcher({ foo: `[0]` }),
{
type: NodeTypes.INTERPOLATION,
content: {
content: `1`,
isStatic: false,
isConstant: true
}
type: NodeTypes.VNODE_CALL,
tag: `"span"`,
props: createObjectMatcher({ foo: `[0]` }),
children: {
type: NodeTypes.INTERPOLATION,
content: {
content: `1`,
isStatic: false,
isConstant: true
}
]
}
}
])
expect(args).toMatchObject([
`"div"`,
`null`,
[
expect(root.codegenNode).toMatchObject({
tag: `"div"`,
props: undefined,
children: [
{
type: NodeTypes.ELEMENT,
codegenNode: {
@@ -565,12 +486,12 @@ describe('compiler: hoistStatic transform', () => {
}
}
]
])
})
expect(generate(root).code).toMatchSnapshot()
})
test('hoist class with static object value', () => {
const { root, args } = transformWithHoist(
const root = transformWithHoist(
`<div><span :class="{ foo: true }">{{ bar }}</span></div>`,
{
prefixIdentifiers: true
@@ -596,39 +517,37 @@ describe('compiler: hoistStatic transform', () => {
]
}
])
expect(args).toMatchObject([
`"div"`,
`null`,
[
expect(root.codegenNode).toMatchObject({
tag: `"div"`,
props: undefined,
children: [
{
type: NodeTypes.ELEMENT,
codegenNode: {
callee: CREATE_VNODE,
arguments: [
`"span"`,
{
type: NodeTypes.SIMPLE_EXPRESSION,
content: `_hoisted_1`
},
{
type: NodeTypes.INTERPOLATION,
content: {
content: `_ctx.bar`,
isConstant: false,
isStatic: false
}
},
`1 /* TEXT */`
]
type: NodeTypes.VNODE_CALL,
tag: `"span"`,
props: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: `_hoisted_1`
},
children: {
type: NodeTypes.INTERPOLATION,
content: {
content: `_ctx.bar`,
isConstant: false,
isStatic: false
}
},
patchFlag: `1 /* TEXT */`
}
}
]
])
})
expect(generate(root).code).toMatchSnapshot()
})
test('should NOT hoist expressions that refer scope variables', () => {
const { root } = transformWithHoist(
const root = transformWithHoist(
`<div><p v-for="o in list"><span>{{ o }}</span></p></div>`,
{
prefixIdentifiers: true
@@ -640,7 +559,7 @@ describe('compiler: hoistStatic transform', () => {
})
test('should NOT hoist expressions that refer scope variables (2)', () => {
const { root } = transformWithHoist(
const root = transformWithHoist(
`<div><p v-for="o in list"><span>{{ o + 'foo' }}</span></p></div>`,
{
prefixIdentifiers: true
@@ -652,7 +571,7 @@ describe('compiler: hoistStatic transform', () => {
})
test('should NOT hoist expressions that refer scope variables (v-slot)', () => {
const { root } = transformWithHoist(
const root = transformWithHoist(
`<Comp v-slot="{ foo }">{{ foo }}</Comp>`,
{
prefixIdentifiers: true
@@ -664,7 +583,7 @@ describe('compiler: hoistStatic transform', () => {
})
test('should NOT hoist elements with cached handlers', () => {
const { root } = transformWithHoist(
const root = transformWithHoist(
`<div><div><div @click="foo"/></div></div>`,
{
prefixIdentifiers: true,

View File

@@ -0,0 +1,24 @@
import {
baseParse as parse,
transform,
ElementNode,
noopDirectiveTransform,
VNodeCall
} from '../../src'
import { transformElement } from '../../src/transforms/transformElement'
describe('compiler: noop directive transform', () => {
test('should add no props to DOM', () => {
const ast = parse(`<div v-noop/>`)
transform(ast, {
nodeTransforms: [transformElement],
directiveTransforms: {
noop: noopDirectiveTransform
}
})
const node = ast.children[0] as ElementNode
// As v-noop adds no properties the codegen should be identical to
// rendering a div with no props or reactive data (so just the tag as the arg)
expect((node.codegenNode as VNodeCall).props).toBeUndefined()
})
})

View File

@@ -1,24 +1,28 @@
import { CompilerOptions, parse, transform, ErrorCodes } from '../../src'
import {
CompilerOptions,
baseParse as parse,
transform,
ErrorCodes
} from '../../src'
import {
RESOLVE_COMPONENT,
CREATE_VNODE,
MERGE_PROPS,
RESOLVE_DIRECTIVE,
WITH_DIRECTIVES,
TO_HANDLERS,
helperNameMap,
PORTAL,
TELEPORT,
RESOLVE_DYNAMIC_COMPONENT,
SUSPENSE,
KEEP_ALIVE,
BASE_TRANSITION
} from '../../src/runtimeHelpers'
import {
CallExpression,
NodeTypes,
createObjectProperty,
DirectiveNode,
RootNode
RootNode,
VNodeCall
} from '../../src/ast'
import { transformElement } from '../../src/transforms/transformElement'
import { transformStyle } from '../../../compiler-dom/src/transforms/transformStyle'
@@ -33,7 +37,7 @@ function parseWithElementTransform(
options: CompilerOptions = {}
): {
root: RootNode
node: CallExpression
node: VNodeCall
} {
// wrap raw template in an extra div so that it doesn't get turned into a
// block as root node
@@ -43,8 +47,8 @@ function parseWithElementTransform(
...options
})
const codegenNode = (ast as any).children[0].children[0]
.codegenNode as CallExpression
expect(codegenNode.type).toBe(NodeTypes.JS_CALL_EXPRESSION)
.codegenNode as VNodeCall
expect(codegenNode.type).toBe(NodeTypes.VNODE_CALL)
return {
root: ast,
node: codegenNode
@@ -68,63 +72,63 @@ describe('compiler: element transform', () => {
test('static props', () => {
const { node } = parseWithElementTransform(`<div id="foo" class="bar" />`)
expect(node.callee).toBe(CREATE_VNODE)
expect(node.arguments).toMatchObject([
`"div"`,
createObjectMatcher({
expect(node).toMatchObject({
tag: `"div"`,
props: createObjectMatcher({
id: 'foo',
class: 'bar'
})
])
}),
children: undefined
})
})
test('props + children', () => {
const { node } = parseWithElementTransform(`<div id="foo"><span/></div>`)
expect(node.callee).toBe(CREATE_VNODE)
expect(node.arguments).toMatchObject([
`"div"`,
createObjectMatcher({
expect(node).toMatchObject({
tag: `"div"`,
props: createObjectMatcher({
id: 'foo'
}),
[
children: [
{
type: NodeTypes.ELEMENT,
tag: 'span',
codegenNode: {
callee: CREATE_VNODE,
arguments: [`"span"`]
type: NodeTypes.VNODE_CALL,
tag: `"span"`
}
}
]
])
})
})
test('0 placeholder for children with no props', () => {
const { node } = parseWithElementTransform(`<div><span/></div>`)
expect(node.callee).toBe(CREATE_VNODE)
expect(node.arguments).toMatchObject([
`"div"`,
`null`,
[
expect(node).toMatchObject({
tag: `"div"`,
props: undefined,
children: [
{
type: NodeTypes.ELEMENT,
tag: 'span',
codegenNode: {
callee: CREATE_VNODE,
arguments: [`"span"`]
type: NodeTypes.VNODE_CALL,
tag: `"span"`
}
}
]
])
})
})
test('v-bind="obj"', () => {
const { root, node } = parseWithElementTransform(`<div v-bind="obj" />`)
// single v-bind doesn't need mergeProps
expect(root.helpers).not.toContain(MERGE_PROPS)
expect(node.callee).toBe(CREATE_VNODE)
// should directly use `obj` in props position
expect(node.arguments[1]).toMatchObject({
expect(node.props).toMatchObject({
type: NodeTypes.SIMPLE_EXPRESSION,
content: `obj`
})
@@ -135,8 +139,8 @@ describe('compiler: element transform', () => {
`<div id="foo" v-bind="obj" />`
)
expect(root.helpers).toContain(MERGE_PROPS)
expect(node.callee).toBe(CREATE_VNODE)
expect(node.arguments[1]).toMatchObject({
expect(node.props).toMatchObject({
type: NodeTypes.JS_CALL_EXPRESSION,
callee: MERGE_PROPS,
arguments: [
@@ -156,8 +160,8 @@ describe('compiler: element transform', () => {
`<div v-bind="obj" id="foo" />`
)
expect(root.helpers).toContain(MERGE_PROPS)
expect(node.callee).toBe(CREATE_VNODE)
expect(node.arguments[1]).toMatchObject({
expect(node.props).toMatchObject({
type: NodeTypes.JS_CALL_EXPRESSION,
callee: MERGE_PROPS,
arguments: [
@@ -177,8 +181,8 @@ describe('compiler: element transform', () => {
`<div id="foo" v-bind="obj" class="bar" />`
)
expect(root.helpers).toContain(MERGE_PROPS)
expect(node.callee).toBe(CREATE_VNODE)
expect(node.arguments[1]).toMatchObject({
expect(node.props).toMatchObject({
type: NodeTypes.JS_CALL_EXPRESSION,
callee: MERGE_PROPS,
arguments: [
@@ -201,8 +205,8 @@ describe('compiler: element transform', () => {
`<div id="foo" v-on="obj" class="bar" />`
)
expect(root.helpers).toContain(MERGE_PROPS)
expect(node.callee).toBe(CREATE_VNODE)
expect(node.arguments[1]).toMatchObject({
expect(node.props).toMatchObject({
type: NodeTypes.JS_CALL_EXPRESSION,
callee: MERGE_PROPS,
arguments: [
@@ -231,8 +235,8 @@ describe('compiler: element transform', () => {
`<div id="foo" v-on="handlers" v-bind="obj" />`
)
expect(root.helpers).toContain(MERGE_PROPS)
expect(node.callee).toBe(CREATE_VNODE)
expect(node.arguments[1]).toMatchObject({
expect(node.props).toMatchObject({
type: NodeTypes.JS_CALL_EXPRESSION,
callee: MERGE_PROPS,
arguments: [
@@ -259,43 +263,43 @@ describe('compiler: element transform', () => {
test('should handle plain <template> as normal element', () => {
const { node } = parseWithElementTransform(`<template id="foo" />`)
expect(node.callee).toBe(CREATE_VNODE)
expect(node.arguments).toMatchObject([
`"template"`,
createObjectMatcher({
expect(node).toMatchObject({
tag: `"template"`,
props: createObjectMatcher({
id: 'foo'
})
])
})
})
test('should handle <Portal> with normal children', () => {
test('should handle <Teleport> with normal children', () => {
function assert(tag: string) {
const { root, node } = parseWithElementTransform(
`<${tag} target="#foo"><span /></${tag}>`
)
expect(root.components.length).toBe(0)
expect(root.helpers).toContain(PORTAL)
expect(node.callee).toBe(CREATE_VNODE)
expect(node.arguments).toMatchObject([
PORTAL,
createObjectMatcher({
expect(root.helpers).toContain(TELEPORT)
expect(node).toMatchObject({
tag: TELEPORT,
props: createObjectMatcher({
target: '#foo'
}),
[
children: [
{
type: NodeTypes.ELEMENT,
tag: 'span',
codegenNode: {
callee: CREATE_VNODE,
arguments: [`"span"`]
type: NodeTypes.VNODE_CALL,
tag: `"span"`
}
}
]
])
})
}
assert(`portal`)
assert(`Portal`)
assert(`teleport`)
assert(`Teleport`)
})
test('should handle <Suspense>', () => {
@@ -305,11 +309,11 @@ describe('compiler: element transform', () => {
)
expect(root.components.length).toBe(0)
expect(root.helpers).toContain(SUSPENSE)
expect(node.callee).toBe(CREATE_VNODE)
expect(node.arguments).toMatchObject([
SUSPENSE,
`null`,
hasFallback
expect(node).toMatchObject({
tag: SUSPENSE,
props: undefined,
children: hasFallback
? createObjectMatcher({
default: {
type: NodeTypes.JS_FUNCTION_EXPRESSION
@@ -317,15 +321,15 @@ describe('compiler: element transform', () => {
fallback: {
type: NodeTypes.JS_FUNCTION_EXPRESSION
},
_compiled: `[true]`
_: `[1]`
})
: createObjectMatcher({
default: {
type: NodeTypes.JS_FUNCTION_EXPRESSION
},
_compiled: `[true]`
_: `[1]`
})
])
})
}
assert(`suspense`, `foo`)
@@ -339,18 +343,23 @@ describe('compiler: element transform', () => {
test('should handle <KeepAlive>', () => {
function assert(tag: string) {
const { root, node } = parseWithElementTransform(
`<${tag}><span /></${tag}>`
)
const root = parse(`<div><${tag}><span /></${tag}></div>`)
transform(root, {
nodeTransforms: [transformElement, transformText]
})
expect(root.components.length).toBe(0)
expect(root.helpers).toContain(KEEP_ALIVE)
expect(node.callee).toBe(CREATE_VNODE)
expect(node.arguments).toMatchObject([
KEEP_ALIVE,
`null`,
const node = (root.children[0] as any).children[0].codegenNode
expect(node).toMatchObject({
type: NodeTypes.VNODE_CALL,
tag: KEEP_ALIVE,
isBlock: true, // should be forced into a block
props: undefined,
// keep-alive should not compile content to slots
[{ type: NodeTypes.ELEMENT, tag: 'span' }]
])
children: [{ type: NodeTypes.ELEMENT, tag: 'span' }],
// should get a dynamic slots flag to force updates
patchFlag: genFlagText(PatchFlags.DYNAMIC_SLOTS)
})
}
assert(`keep-alive`)
@@ -364,17 +373,17 @@ describe('compiler: element transform', () => {
)
expect(root.components.length).toBe(0)
expect(root.helpers).toContain(BASE_TRANSITION)
expect(node.callee).toBe(CREATE_VNODE)
expect(node.arguments).toMatchObject([
BASE_TRANSITION,
`null`,
createObjectMatcher({
expect(node).toMatchObject({
tag: BASE_TRANSITION,
props: undefined,
children: createObjectMatcher({
default: {
type: NodeTypes.JS_FUNCTION_EXPRESSION
},
_compiled: `[true]`
_: `[1]`
})
])
})
}
assert(`base-transition`)
@@ -398,14 +407,13 @@ describe('compiler: element transform', () => {
foo(dir) {
_dir = dir
return {
props: [createObjectProperty(dir.arg!, dir.exp!)],
needRuntime: false
props: [createObjectProperty(dir.arg!, dir.exp!)]
}
}
}
})
expect(node.callee).toBe(CREATE_VNODE)
expect(node.arguments[1]).toMatchObject({
expect(node.props).toMatchObject({
type: NodeTypes.JS_OBJECT_EXPRESSION,
properties: [
{
@@ -417,8 +425,8 @@ describe('compiler: element transform', () => {
})
// should factor in props returned by custom directive transforms
// in patchFlag analysis
expect(node.arguments[3]).toMatch(PatchFlags.PROPS + '')
expect(node.arguments[4]).toMatch(`"bar"`)
expect(node.patchFlag).toMatch(PatchFlags.PROPS + '')
expect(node.dynamicProps).toMatch(`"bar"`)
})
test('directiveTransform with needRuntime: true', () => {
@@ -437,20 +445,12 @@ describe('compiler: element transform', () => {
)
expect(root.helpers).toContain(RESOLVE_DIRECTIVE)
expect(root.directives).toContain(`foo`)
expect(node.callee).toBe(WITH_DIRECTIVES)
expect(node.arguments).toMatchObject([
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_VNODE,
arguments: [
`"div"`,
`null`,
`null`,
genFlagText(PatchFlags.NEED_PATCH) // should generate appropriate flag
]
},
{
expect(node).toMatchObject({
tag: `"div"`,
props: undefined,
children: undefined,
patchFlag: genFlagText(PatchFlags.NEED_PATCH), // should generate appropriate flag
directives: {
type: NodeTypes.JS_ARRAY_EXPRESSION,
elements: [
{
@@ -473,7 +473,7 @@ describe('compiler: element transform', () => {
}
]
}
])
})
})
test('directiveTransform with needRuntime: Symbol', () => {
@@ -494,7 +494,7 @@ describe('compiler: element transform', () => {
expect(root.helpers).toContain(CREATE_VNODE)
expect(root.helpers).not.toContain(RESOLVE_DIRECTIVE)
expect(root.directives.length).toBe(0)
expect((node as any).arguments[1].elements[0].elements[0]).toBe(
expect(node.directives!.elements[0].elements[0]).toBe(
`_${helperNameMap[CREATE_VNODE]}`
)
})
@@ -508,12 +508,8 @@ describe('compiler: element transform', () => {
expect(root.directives).toContain(`bar`)
expect(root.directives).toContain(`baz`)
expect(node.callee).toBe(WITH_DIRECTIVES)
expect(node.arguments).toMatchObject([
{
type: NodeTypes.JS_CALL_EXPRESSION
},
{
expect(node).toMatchObject({
directives: {
type: NodeTypes.JS_ARRAY_EXPRESSION,
elements: [
{
@@ -583,7 +579,7 @@ describe('compiler: element transform', () => {
}
]
}
])
})
})
test(`props merging: event handlers`, () => {
@@ -595,7 +591,7 @@ describe('compiler: element transform', () => {
}
}
)
expect(node.arguments[1]).toMatchObject({
expect(node.props).toMatchObject({
type: NodeTypes.JS_OBJECT_EXPRESSION,
properties: [
{
@@ -627,7 +623,7 @@ describe('compiler: element transform', () => {
test(`props merging: style`, () => {
const { node } = parseWithElementTransform(
`<div style="color: red" :style="{ color: 'red' }" />`,
`<div style="color: green" :style="{ color: 'red' }" />`,
{
nodeTransforms: [transformStyle, transformElement],
directiveTransforms: {
@@ -635,7 +631,7 @@ describe('compiler: element transform', () => {
}
}
)
expect(node.arguments[1]).toMatchObject({
expect(node.props).toMatchObject({
type: NodeTypes.JS_OBJECT_EXPRESSION,
properties: [
{
@@ -650,7 +646,7 @@ describe('compiler: element transform', () => {
elements: [
{
type: NodeTypes.SIMPLE_EXPRESSION,
content: `_hoisted_1`,
content: `{"color":"green"}`,
isStatic: false
},
{
@@ -674,7 +670,7 @@ describe('compiler: element transform', () => {
}
}
)
expect(node.arguments[1]).toMatchObject({
expect(node.props).toMatchObject({
type: NodeTypes.JS_OBJECT_EXPRESSION,
properties: [
{
@@ -707,113 +703,175 @@ describe('compiler: element transform', () => {
describe('patchFlag analysis', () => {
test('TEXT', () => {
const { node } = parseWithBind(`<div>foo</div>`)
expect(node.arguments.length).toBe(3)
expect(node.patchFlag).toBeUndefined()
const { node: node2 } = parseWithBind(`<div>{{ foo }}</div>`)
expect(node2.arguments.length).toBe(4)
expect(node2.arguments[3]).toBe(genFlagText(PatchFlags.TEXT))
expect(node2.patchFlag).toBe(genFlagText(PatchFlags.TEXT))
// multiple nodes, merged with optimize text
const { node: node3 } = parseWithBind(`<div>foo {{ bar }} baz</div>`)
expect(node3.arguments.length).toBe(4)
expect(node3.arguments[3]).toBe(genFlagText(PatchFlags.TEXT))
expect(node3.patchFlag).toBe(genFlagText(PatchFlags.TEXT))
})
test('CLASS', () => {
const { node } = parseWithBind(`<div :class="foo" />`)
expect(node.arguments.length).toBe(4)
expect(node.arguments[3]).toBe(genFlagText(PatchFlags.CLASS))
expect(node.patchFlag).toBe(genFlagText(PatchFlags.CLASS))
})
test('STYLE', () => {
const { node } = parseWithBind(`<div :style="foo" />`)
expect(node.arguments.length).toBe(4)
expect(node.arguments[3]).toBe(genFlagText(PatchFlags.STYLE))
expect(node.patchFlag).toBe(genFlagText(PatchFlags.STYLE))
})
test('PROPS', () => {
const { node } = parseWithBind(`<div id="foo" :foo="bar" :baz="qux" />`)
expect(node.arguments.length).toBe(5)
expect(node.arguments[3]).toBe(genFlagText(PatchFlags.PROPS))
expect(node.arguments[4]).toBe(`["foo", "baz"]`)
expect(node.patchFlag).toBe(genFlagText(PatchFlags.PROPS))
expect(node.dynamicProps).toBe(`["foo", "baz"]`)
})
test('CLASS + STYLE + PROPS', () => {
const { node } = parseWithBind(
`<div id="foo" :class="cls" :style="styl" :foo="bar" :baz="qux"/>`
)
expect(node.arguments.length).toBe(5)
expect(node.arguments[3]).toBe(
expect(node.patchFlag).toBe(
genFlagText([PatchFlags.CLASS, PatchFlags.STYLE, PatchFlags.PROPS])
)
expect(node.arguments[4]).toBe(`["foo", "baz"]`)
expect(node.dynamicProps).toBe(`["foo", "baz"]`)
})
test('FULL_PROPS (v-bind)', () => {
const { node } = parseWithBind(`<div v-bind="foo" />`)
expect(node.arguments.length).toBe(4)
expect(node.arguments[3]).toBe(genFlagText(PatchFlags.FULL_PROPS))
expect(node.patchFlag).toBe(genFlagText(PatchFlags.FULL_PROPS))
})
test('FULL_PROPS (dynamic key)', () => {
const { node } = parseWithBind(`<div :[foo]="bar" />`)
expect(node.arguments.length).toBe(4)
expect(node.arguments[3]).toBe(genFlagText(PatchFlags.FULL_PROPS))
expect(node.patchFlag).toBe(genFlagText(PatchFlags.FULL_PROPS))
})
test('FULL_PROPS (w/ others)', () => {
const { node } = parseWithBind(
`<div id="foo" v-bind="bar" :class="cls" />`
)
expect(node.arguments.length).toBe(4)
expect(node.arguments[3]).toBe(genFlagText(PatchFlags.FULL_PROPS))
expect(node.patchFlag).toBe(genFlagText(PatchFlags.FULL_PROPS))
})
test('NEED_PATCH (static ref)', () => {
const { node } = parseWithBind(`<div ref="foo" />`)
expect(node.arguments.length).toBe(4)
expect(node.arguments[3]).toBe(genFlagText(PatchFlags.NEED_PATCH))
expect(node.patchFlag).toBe(genFlagText(PatchFlags.NEED_PATCH))
})
test('NEED_PATCH (dynamic ref)', () => {
const { node } = parseWithBind(`<div :ref="foo" />`)
expect(node.arguments.length).toBe(4)
expect(node.arguments[3]).toBe(genFlagText(PatchFlags.NEED_PATCH))
expect(node.patchFlag).toBe(genFlagText(PatchFlags.NEED_PATCH))
})
test('NEED_PATCH (custom directives)', () => {
const { node } = parseWithBind(`<div v-foo />`)
const vnodeCall = node.arguments[0] as CallExpression
expect(vnodeCall.arguments.length).toBe(4)
expect(vnodeCall.arguments[3]).toBe(genFlagText(PatchFlags.NEED_PATCH))
expect(node.patchFlag).toBe(genFlagText(PatchFlags.NEED_PATCH))
})
test('HYDRATE_EVENTS', () => {
// ignore click events (has dedicated fast path)
const { node } = parseWithElementTransform(`<div @click="foo" />`, {
directiveTransforms: {
on: transformOn
}
})
// should only have props flag
expect(node.patchFlag).toBe(genFlagText(PatchFlags.PROPS))
const { node: node2 } = parseWithElementTransform(
`<div @keyup="foo" />`,
{
directiveTransforms: {
on: transformOn
}
}
)
expect(node2.patchFlag).toBe(
genFlagText([PatchFlags.PROPS, PatchFlags.HYDRATE_EVENTS])
)
})
})
describe('dynamic component', () => {
test('static binding', () => {
const { node, root } = parseWithBind(`<component is="foo" />`)
expect(root.helpers).not.toContain(RESOLVE_DYNAMIC_COMPONENT)
expect(root.helpers).toContain(RESOLVE_DYNAMIC_COMPONENT)
expect(node).toMatchObject({
callee: CREATE_VNODE,
arguments: ['_component_foo']
tag: {
callee: RESOLVE_DYNAMIC_COMPONENT,
arguments: [
{
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'foo',
isStatic: true
}
]
}
})
})
test('dynamic binding', () => {
const { node, root } = parseWithBind(`<component :is="foo" />`)
expect(root.helpers).toContain(RESOLVE_DYNAMIC_COMPONENT)
expect(node.arguments).toMatchObject([
{
expect(node).toMatchObject({
tag: {
callee: RESOLVE_DYNAMIC_COMPONENT,
arguments: [
{
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'foo'
},
'$'
content: 'foo',
isStatic: false
}
]
}
])
})
})
test('v-is', () => {
const { node, root } = parseWithBind(`<div v-is="'foo'" />`)
expect(root.helpers).toContain(RESOLVE_DYNAMIC_COMPONENT)
expect(node).toMatchObject({
tag: {
callee: RESOLVE_DYNAMIC_COMPONENT,
arguments: [
{
type: NodeTypes.SIMPLE_EXPRESSION,
content: `'foo'`,
isStatic: false
}
]
},
// should skip v-is runtime check
directives: undefined
})
})
})
test('<svg> should be forced into blocks', () => {
const ast = parse(`<div><svg/></div>`)
transform(ast, {
nodeTransforms: [transformElement]
})
expect((ast as any).children[0].children[0].codegenNode).toMatchObject({
type: NodeTypes.VNODE_CALL,
tag: `"svg"`,
isBlock: true
})
})
// #938
test('element with dynamic keys should be forced into blocks', () => {
const ast = parse(`<div><div :key="foo" /></div>`)
transform(ast, {
nodeTransforms: [transformElement]
})
expect((ast as any).children[0].children[0].codegenNode).toMatchObject({
type: NodeTypes.VNODE_CALL,
tag: `"div"`,
isBlock: true
})
})
})

View File

@@ -1,5 +1,5 @@
import {
parse,
baseParse as parse,
transform,
ElementNode,
DirectiveNode,
@@ -317,6 +317,16 @@ describe('compiler: expression transform', () => {
})
})
test('should not duplicate object key with same name as value', () => {
const node = parseWithExpressionTransform(
`{{ { foo: foo } }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [`{ foo: `, { content: `_ctx.foo` }, ` }`]
})
})
test('should prefix a computed object property key', () => {
const node = parseWithExpressionTransform(
`{{ { [foo]: bar } }}`
@@ -380,7 +390,65 @@ describe('compiler: expression transform', () => {
const onError = jest.fn()
parseWithExpressionTransform(`{{ a( }}`, { onError })
expect(onError.mock.calls[0][0].message).toMatch(
`Invalid JavaScript expression. (1:4)`
`Error parsing JavaScript expression: Unexpected token`
)
})
describe('ES Proposals support', () => {
test('bigInt', () => {
const node = parseWithExpressionTransform(
`{{ 13000n }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.SIMPLE_EXPRESSION,
content: `13000n`,
isStatic: false,
isConstant: true
})
})
test('nullish colescing', () => {
const node = parseWithExpressionTransform(
`{{ a ?? b }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [{ content: `_ctx.a` }, ` ?? `, { content: `_ctx.b` }]
})
})
test('optional chaining', () => {
const node = parseWithExpressionTransform(
`{{ a?.b?.c }}`
) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
{ content: `_ctx.a` },
`?.`,
{ content: `b` },
`?.`,
{ content: `c` }
]
})
})
test('Enabling additional plugins', () => {
// enabling pipeline operator to replace filters:
const node = parseWithExpressionTransform(`{{ a |> uppercase }}`, {
expressionPlugins: [
[
'pipelineOperator',
{
proposal: 'minimal'
}
]
]
}) as InterpolationNode
expect(node.content).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
children: [{ content: `_ctx.a` }, ` |> `, { content: `_ctx.uppercase` }]
})
})
})
})

View File

@@ -1,6 +1,6 @@
import {
CompilerOptions,
parse,
baseParse as parse,
transform,
ElementNode,
NodeTypes,

View File

@@ -1,6 +1,6 @@
import {
CompilerOptions,
parse,
baseParse as parse,
transform,
NodeTypes,
generate,

View File

@@ -1,11 +1,11 @@
import {
parse,
baseParse as parse,
transform,
ElementNode,
ObjectExpression,
CompilerOptions,
ErrorCodes,
CallExpression
VNodeCall
} from '../../src'
import { transformBind } from '../../src/transforms/vBind'
import { transformElement } from '../../src/transforms/transformElement'
@@ -33,8 +33,7 @@ function parseWithVBind(
describe('compiler: transform v-bind', () => {
test('basic', () => {
const node = parseWithVBind(`<div v-bind:id="id"/>`)
const props = (node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression
const props = (node.codegenNode as VNodeCall).props as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: {
content: `id`,
@@ -69,8 +68,7 @@ describe('compiler: transform v-bind', () => {
test('dynamic arg', () => {
const node = parseWithVBind(`<div v-bind:[id]="id"/>`)
const props = (node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression
const props = (node.codegenNode as VNodeCall).props as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: {
content: `id`,
@@ -103,8 +101,7 @@ describe('compiler: transform v-bind', () => {
test('.camel modifier', () => {
const node = parseWithVBind(`<div v-bind:foo-bar.camel="id"/>`)
const props = (node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression
const props = (node.codegenNode as VNodeCall).props as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: {
content: `fooBar`,
@@ -119,8 +116,7 @@ describe('compiler: transform v-bind', () => {
test('.camel modifier w/ dynamic arg', () => {
const node = parseWithVBind(`<div v-bind:[foo].camel="id"/>`)
const props = (node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression
const props = (node.codegenNode as VNodeCall).props as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: {
content: `_${helperNameMap[CAMELIZE]}(foo)`,
@@ -137,12 +133,11 @@ describe('compiler: transform v-bind', () => {
const node = parseWithVBind(`<div v-bind:[foo(bar)].camel="id"/>`, {
prefixIdentifiers: true
})
const props = (node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression
const props = (node.codegenNode as VNodeCall).props as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: {
children: [
`${helperNameMap[CAMELIZE]}(`,
`_${helperNameMap[CAMELIZE]}(`,
{ content: `_ctx.foo` },
`(`,
{ content: `_ctx.bar` },

View File

@@ -1,4 +1,4 @@
import { parse } from '../../src/parse'
import { baseParse as parse } from '../../src/parse'
import { transform } from '../../src/transform'
import { transformIf } from '../../src/transforms/vIf'
import { transformFor } from '../../src/transforms/vFor'
@@ -12,19 +12,11 @@ import {
SimpleExpressionNode,
ElementNode,
InterpolationNode,
CallExpression,
SequenceExpression
ForCodegenNode
} from '../../src/ast'
import { ErrorCodes } from '../../src/errors'
import { CompilerOptions, generate } from '../../src'
import {
OPEN_BLOCK,
CREATE_BLOCK,
FRAGMENT,
RENDER_LIST,
RENDER_SLOT,
WITH_DIRECTIVES
} from '../../src/runtimeHelpers'
import { FRAGMENT, RENDER_LIST, RENDER_SLOT } from '../../src/runtimeHelpers'
import { PatchFlags } from '@vue/shared'
import { createObjectMatcher, genFlagText } from '../testUtils'
@@ -48,7 +40,7 @@ function parseWithForTransform(
})
return {
root: ast,
node: ast.children[0] as ForNode
node: ast.children[0] as ForNode & { codegenNode: ForCodegenNode }
}
}
@@ -566,64 +558,40 @@ describe('compiler: v-for', () => {
describe('codegen', () => {
function assertSharedCodegen(
node: SequenceExpression,
node: ForCodegenNode,
keyed: boolean = false,
customReturn: boolean = false
) {
expect(node).toMatchObject({
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
expressions: [
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: OPEN_BLOCK
},
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_BLOCK,
arguments: [
FRAGMENT,
`null`,
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: RENDER_LIST,
arguments: [
{}, // to be asserted by each test
{
type: NodeTypes.JS_FUNCTION_EXPRESSION,
returns: customReturn
? {}
: {
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
expressions: [
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: OPEN_BLOCK
},
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_BLOCK
}
]
}
type: NodeTypes.VNODE_CALL,
tag: FRAGMENT,
isForBlock: true,
patchFlag: keyed
? genFlagText(PatchFlags.KEYED_FRAGMENT)
: genFlagText(PatchFlags.UNKEYED_FRAGMENT),
children: {
type: NodeTypes.JS_CALL_EXPRESSION,
callee: RENDER_LIST,
arguments: [
{}, // to be asserted by each test
{
type: NodeTypes.JS_FUNCTION_EXPRESSION,
returns: customReturn
? {}
: {
type: NodeTypes.VNODE_CALL,
isBlock: true
}
]
},
keyed
? genFlagText(PatchFlags.KEYED_FRAGMENT)
: genFlagText(PatchFlags.UNKEYED_FRAGMENT)
]
}
]
}
]
}
})
const renderListArgs = ((node.expressions[1] as CallExpression)
.arguments[2] as CallExpression).arguments
const renderListArgs = node.children.arguments
return {
source: renderListArgs[0] as SimpleExpressionNode,
params: (renderListArgs[1] as any).params,
returns: (renderListArgs[1] as any).returns,
blockArgs: customReturn
? null
: (renderListArgs[1] as any).returns.expressions[1].arguments
innerVNodeCall: customReturn ? null : (renderListArgs[1] as any).returns
}
}
@@ -635,7 +603,9 @@ describe('compiler: v-for', () => {
expect(assertSharedCodegen(codegenNode)).toMatchObject({
source: { content: `items` },
params: [{ content: `item` }],
blockArgs: [`"span"`]
innerVNodeCall: {
tag: `"span"`
}
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -698,15 +668,16 @@ describe('compiler: v-for', () => {
expect(assertSharedCodegen(codegenNode)).toMatchObject({
source: { content: `items` },
params: [{ content: `item` }],
blockArgs: [
FRAGMENT,
`null`,
[
innerVNodeCall: {
tag: FRAGMENT,
props: undefined,
isBlock: true,
children: [
{ type: NodeTypes.TEXT, content: `hello` },
{ type: NodeTypes.ELEMENT, tag: `span` }
],
genFlagText(PatchFlags.STABLE_FRAGMENT)
]
patchFlag: genFlagText(PatchFlags.STABLE_FRAGMENT)
}
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -757,12 +728,12 @@ describe('compiler: v-for', () => {
expect(assertSharedCodegen(codegenNode, true)).toMatchObject({
source: { content: `items` },
params: [{ content: `item` }],
blockArgs: [
`"span"`,
createObjectMatcher({
innerVNodeCall: {
tag: `"span"`,
props: createObjectMatcher({
key: `[item]`
})
]
}
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -777,17 +748,17 @@ describe('compiler: v-for', () => {
expect(assertSharedCodegen(codegenNode, true)).toMatchObject({
source: { content: `items` },
params: [{ content: `item` }],
blockArgs: [
FRAGMENT,
createObjectMatcher({
innerVNodeCall: {
tag: FRAGMENT,
props: createObjectMatcher({
key: `[item]`
}),
[
children: [
{ type: NodeTypes.TEXT, content: `hello` },
{ type: NodeTypes.ELEMENT, tag: `span` }
],
genFlagText(PatchFlags.STABLE_FRAGMENT)
]
patchFlag: genFlagText(PatchFlags.STABLE_FRAGMENT)
}
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -798,53 +769,33 @@ describe('compiler: v-for', () => {
node: { codegenNode }
} = parseWithForTransform(`<div v-if="ok" v-for="i in list"/>`)
expect(codegenNode).toMatchObject({
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
expressions: [
{
type: NodeTypes.JS_CONDITIONAL_EXPRESSION,
test: { content: `ok` },
consequent: {
type: NodeTypes.VNODE_CALL,
props: createObjectMatcher({
key: `[0]`
}),
isBlock: true,
isForBlock: true,
patchFlag: genFlagText(PatchFlags.UNKEYED_FRAGMENT),
children: {
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,
// should optimize v-if + v-for into a single Fragment block
arguments: [
FRAGMENT,
createObjectMatcher({ key: `[0]` }),
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: RENDER_LIST,
arguments: [
{ content: `list` },
{
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: [{ content: `i` }],
returns: {
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
expressions: [
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: OPEN_BLOCK
},
{
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_BLOCK,
arguments: [`"div"`]
}
]
}
}
]
},
genFlagText(PatchFlags.UNKEYED_FRAGMENT)
]
}
callee: RENDER_LIST,
arguments: [
{ content: `list` },
{
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: [{ content: `i` }],
returns: {
type: NodeTypes.VNODE_CALL,
tag: `"div"`,
isBlock: true
}
}
]
}
]
}
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -856,18 +807,8 @@ describe('compiler: v-for', () => {
} = parseWithForTransform('<div v-for="i in list" v-foo/>')
const { returns } = assertSharedCodegen(codegenNode, false, true)
expect(returns).toMatchObject({
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
expressions: [
{ callee: OPEN_BLOCK },
// should wrap withDirectives() around createBlock()
{
callee: WITH_DIRECTIVES,
arguments: [
{ callee: CREATE_BLOCK },
{ type: NodeTypes.JS_ARRAY_EXPRESSION }
]
}
]
type: NodeTypes.VNODE_CALL,
directives: { type: NodeTypes.JS_ARRAY_EXPRESSION }
})
expect(generate(root).code).toMatchSnapshot()
})

View File

@@ -1,4 +1,4 @@
import { parse } from '../../src/parse'
import { baseParse as parse } from '../../src/parse'
import { transform } from '../../src/transform'
import { transformIf } from '../../src/transforms/vIf'
import { transformElement } from '../../src/transforms/transformElement'
@@ -10,18 +10,16 @@ import {
TextNode,
CommentNode,
SimpleExpressionNode,
SequenceExpression,
ConditionalExpression,
CallExpression
IfConditionalExpression,
VNodeCall,
ElementTypes
} from '../../src/ast'
import { ErrorCodes } from '../../src/errors'
import { CompilerOptions, generate } from '../../src'
import {
OPEN_BLOCK,
CREATE_BLOCK,
FRAGMENT,
MERGE_PROPS,
WITH_DIRECTIVES,
RENDER_SLOT,
CREATE_COMMENT
} from '../../src/runtimeHelpers'
@@ -43,7 +41,9 @@ function parseWithIfTransform(
}
return {
root: ast,
node: ast.children[returnIndex] as IfNode
node: ast.children[returnIndex] as IfNode & {
codegenNode: IfConditionalExpression
}
}
}
@@ -79,6 +79,22 @@ describe('compiler: v-if', () => {
expect((node.branches[0].children[2] as ElementNode).tag).toBe(`p`)
})
test('component v-if', () => {
const { node } = parseWithIfTransform(`<Component v-if="ok"></Component>`)
expect(node.type).toBe(NodeTypes.IF)
expect(node.branches.length).toBe(1)
expect((node.branches[0].children[0] as ElementNode).tag).toBe(
`Component`
)
expect((node.branches[0].children[0] as ElementNode).tagType).toBe(
ElementTypes.COMPONENT
)
expect(
((node.branches[0].children[0] as ElementNode)!
.codegenNode as VNodeCall)!.isBlock
).toBe(false)
})
test('v-if + v-else', () => {
const { node } = parseWithIfTransform(`<div v-if="ok"/><p v-else/>`)
expect(node.type).toBe(NodeTypes.IF)
@@ -266,49 +282,49 @@ describe('compiler: v-if', () => {
describe('codegen', () => {
function assertSharedCodegen(
node: SequenceExpression,
node: IfConditionalExpression,
depth: number = 0,
hasElse: boolean = false
) {
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: hasElse ? CREATE_BLOCK : CREATE_COMMENT
}
: {
type: NodeTypes.JS_CONDITIONAL_EXPRESSION,
test: {
content: `orNot`
},
consequent: {
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_BLOCK
},
alternate: {
type: NodeTypes.JS_CALL_EXPRESSION,
callee: hasElse ? CREATE_BLOCK : CREATE_COMMENT
type: NodeTypes.JS_CONDITIONAL_EXPRESSION,
test: {
content: `ok`
},
consequent: {
type: NodeTypes.VNODE_CALL,
isBlock: true
},
alternate:
depth < 1
? hasElse
? {
type: NodeTypes.VNODE_CALL,
isBlock: true
}
: {
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_COMMENT
}
: {
type: NodeTypes.JS_CONDITIONAL_EXPRESSION,
test: {
content: `orNot`
},
consequent: {
type: NodeTypes.VNODE_CALL,
isBlock: true
},
alternate: hasElse
? {
type: NodeTypes.VNODE_CALL,
isBlock: true
}
}
}
]
: {
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_COMMENT
}
}
})
}
@@ -318,15 +334,11 @@ describe('compiler: v-if', () => {
node: { codegenNode }
} = parseWithIfTransform(`<div v-if="ok"/>`)
assertSharedCodegen(codegenNode)
const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
.consequent as CallExpression
expect(branch1.arguments).toMatchObject([
`"div"`,
createObjectMatcher({ key: `[0]` })
])
const branch2 = (codegenNode.expressions[1] as ConditionalExpression)
.alternate as CallExpression
expect(branch2).toMatchObject({
expect(codegenNode.consequent).toMatchObject({
tag: `"div"`,
props: createObjectMatcher({ key: `[0]` })
})
expect(codegenNode.alternate).toMatchObject({
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_COMMENT
})
@@ -339,20 +351,16 @@ describe('compiler: v-if', () => {
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,
createObjectMatcher({ key: `[0]` }),
[
expect(codegenNode.consequent).toMatchObject({
tag: FRAGMENT,
props: createObjectMatcher({ key: `[0]` }),
children: [
{ 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).toMatchObject({
})
expect(codegenNode.alternate).toMatchObject({
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_COMMENT
})
@@ -364,10 +372,7 @@ describe('compiler: v-if', () => {
root,
node: { codegenNode }
} = parseWithIfTransform(`<template v-if="ok"><slot/></template>`)
// assertSharedCodegen(codegenNode)
const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
.consequent as CallExpression
expect(branch1).toMatchObject({
expect(codegenNode.consequent).toMatchObject({
type: NodeTypes.JS_CALL_EXPRESSION,
callee: RENDER_SLOT,
arguments: ['$slots', '"default"', createObjectMatcher({ key: `[0]` })]
@@ -380,10 +385,7 @@ describe('compiler: v-if', () => {
root,
node: { codegenNode }
} = parseWithIfTransform(`<slot v-if="ok"></slot>`)
// assertSharedCodegen(codegenNode)
const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
.consequent as CallExpression
expect(branch1).toMatchObject({
expect(codegenNode.consequent).toMatchObject({
type: NodeTypes.JS_CALL_EXPRESSION,
callee: RENDER_SLOT,
arguments: ['$slots', '"default"', createObjectMatcher({ key: `[0]` })]
@@ -397,18 +399,14 @@ describe('compiler: v-if', () => {
node: { codegenNode }
} = parseWithIfTransform(`<div v-if="ok"/><p v-else/>`)
assertSharedCodegen(codegenNode, 0, true)
const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
.consequent as CallExpression
expect(branch1.arguments).toMatchObject([
`"div"`,
createObjectMatcher({ key: `[0]` })
])
const branch2 = (codegenNode.expressions[1] as ConditionalExpression)
.alternate as CallExpression
expect(branch2.arguments).toMatchObject([
`"p"`,
createObjectMatcher({ key: `[1]` })
])
expect(codegenNode.consequent).toMatchObject({
tag: `"div"`,
props: createObjectMatcher({ key: `[0]` })
})
expect(codegenNode.alternate).toMatchObject({
tag: `"p"`,
props: createObjectMatcher({ key: `[1]` })
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -418,18 +416,15 @@ describe('compiler: v-if', () => {
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"`,
createObjectMatcher({ key: `[0]` })
])
const branch2 = (codegenNode.expressions[1] as ConditionalExpression)
.alternate as ConditionalExpression
expect((branch2.consequent as CallExpression).arguments).toMatchObject([
`"p"`,
createObjectMatcher({ key: `[1]` })
])
expect(codegenNode.consequent).toMatchObject({
tag: `"div"`,
props: createObjectMatcher({ key: `[0]` })
})
const branch2 = codegenNode.alternate as ConditionalExpression
expect(branch2.consequent).toMatchObject({
tag: `"p"`,
props: createObjectMatcher({ key: `[1]` })
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -441,28 +436,25 @@ describe('compiler: v-if', () => {
`<div v-if="ok"/><p v-else-if="orNot"/><template v-else>fine</template>`
)
assertSharedCodegen(codegenNode, 1, true)
const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
.consequent as CallExpression
expect(branch1.arguments).toMatchObject([
`"div"`,
createObjectMatcher({ key: `[0]` })
])
const branch2 = (codegenNode.expressions[1] as ConditionalExpression)
.alternate as ConditionalExpression
expect((branch2.consequent as CallExpression).arguments).toMatchObject([
`"p"`,
createObjectMatcher({ key: `[1]` })
])
expect((branch2.alternate as CallExpression).arguments).toMatchObject([
FRAGMENT,
createObjectMatcher({ key: `[2]` }),
[
expect(codegenNode.consequent).toMatchObject({
tag: `"div"`,
props: createObjectMatcher({ key: `[0]` })
})
const branch2 = codegenNode.alternate as ConditionalExpression
expect(branch2.consequent).toMatchObject({
tag: `"p"`,
props: createObjectMatcher({ key: `[1]` })
})
expect(branch2.alternate).toMatchObject({
tag: FRAGMENT,
props: createObjectMatcher({ key: `[2]` }),
children: [
{
type: NodeTypes.TEXT,
content: `fine`
}
]
])
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -470,9 +462,8 @@ describe('compiler: v-if', () => {
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({
const branch1 = codegenNode.consequent as VNodeCall
expect(branch1.props).toMatchObject({
type: NodeTypes.JS_CALL_EXPRESSION,
callee: MERGE_PROPS,
arguments: [createObjectMatcher({ key: `[0]` }), { content: `obj` }]
@@ -483,9 +474,8 @@ describe('compiler: v-if', () => {
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({
const branch1 = codegenNode.consequent as VNodeCall
expect(branch1.props).toMatchObject({
type: NodeTypes.JS_CALL_EXPRESSION,
callee: MERGE_PROPS,
arguments: [
@@ -502,9 +492,8 @@ describe('compiler: v-if', () => {
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({
const branch1 = codegenNode.consequent as VNodeCall
expect(branch1.props).toMatchObject({
type: NodeTypes.JS_CALL_EXPRESSION,
callee: MERGE_PROPS,
arguments: [
@@ -521,13 +510,21 @@ describe('compiler: v-if', () => {
const {
node: { codegenNode }
} = parseWithIfTransform(`<div v-if="ok" v-foo />`)
const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
.consequent as CallExpression
expect(branch1.callee).toBe(WITH_DIRECTIVES)
const realBranch = branch1.arguments[0] as CallExpression
expect(realBranch.arguments[1]).toMatchObject(
createObjectMatcher({ key: `[0]` })
)
const branch1 = codegenNode.consequent as VNodeCall
expect(branch1.directives).not.toBeUndefined()
expect(branch1.props).toMatchObject(createObjectMatcher({ key: `[0]` }))
})
test('v-if with key', () => {
const {
root,
node: { codegenNode }
} = parseWithIfTransform(`<div v-if="ok" key="some-key"/>`)
expect(codegenNode.consequent).toMatchObject({
tag: `"div"`,
props: createObjectMatcher({ key: 'some-key' })
})
expect(generate(root).code).toMatchSnapshot()
})
test.todo('with comments')

View File

@@ -1,16 +1,15 @@
import {
parse,
baseParse as parse,
transform,
generate,
ElementNode,
ObjectExpression,
CompilerOptions,
CallExpression,
ForNode,
PlainElementNode,
PlainElementCodegenNode,
ComponentNode,
NodeTypes
NodeTypes,
VNodeCall
} from '../../src'
import { ErrorCodes } from '../../src/errors'
import { transformModel } from '../../src/transforms/vModel'
@@ -43,8 +42,8 @@ describe('compiler: transform v-model', () => {
test('simple exprssion', () => {
const root = parseWithVModel('<input v-model="model" />')
const node = root.children[0] as ElementNode
const props = ((node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression).properties
const props = ((node.codegenNode as VNodeCall).props as ObjectExpression)
.properties
expect(props[0]).toMatchObject({
key: {
@@ -82,8 +81,8 @@ describe('compiler: transform v-model', () => {
prefixIdentifiers: true
})
const node = root.children[0] as ElementNode
const props = ((node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression).properties
const props = ((node.codegenNode as VNodeCall).props as ObjectExpression)
.properties
expect(props[0]).toMatchObject({
key: {
@@ -119,8 +118,8 @@ describe('compiler: transform v-model', () => {
test('compound expression', () => {
const root = parseWithVModel('<input v-model="model[index]" />')
const node = root.children[0] as ElementNode
const props = ((node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression).properties
const props = ((node.codegenNode as VNodeCall).props as ObjectExpression)
.properties
expect(props[0]).toMatchObject({
key: {
@@ -158,8 +157,8 @@ describe('compiler: transform v-model', () => {
prefixIdentifiers: true
})
const node = root.children[0] as ElementNode
const props = ((node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression).properties
const props = ((node.codegenNode as VNodeCall).props as ObjectExpression)
.properties
expect(props[0]).toMatchObject({
key: {
@@ -191,15 +190,19 @@ describe('compiler: transform v-model', () => {
children: [
'$event => (',
{
content: '_ctx.model',
isStatic: false
children: [
{
content: '_ctx.model',
isStatic: false
},
'[',
{
content: '_ctx.index',
isStatic: false
},
']'
]
},
'[',
{
content: '_ctx.index',
isStatic: false
},
']',
' = $event)'
]
}
@@ -211,9 +214,8 @@ describe('compiler: transform v-model', () => {
test('with argument', () => {
const root = parseWithVModel('<input v-model:value="model" />')
const node = root.children[0] as ElementNode
const props = ((node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression).properties
const props = ((node.codegenNode as VNodeCall).props as ObjectExpression)
.properties
expect(props[0]).toMatchObject({
key: {
content: 'value',
@@ -248,8 +250,8 @@ describe('compiler: transform v-model', () => {
test('with dynamic argument', () => {
const root = parseWithVModel('<input v-model:[value]="model" />')
const node = root.children[0] as ElementNode
const props = ((node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression).properties
const props = ((node.codegenNode as VNodeCall).props as ObjectExpression)
.properties
expect(props[0]).toMatchObject({
key: {
@@ -292,8 +294,8 @@ describe('compiler: transform v-model', () => {
prefixIdentifiers: true
})
const node = root.children[0] as ElementNode
const props = ((node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression).properties
const props = ((node.codegenNode as VNodeCall).props as ObjectExpression)
.properties
expect(props[0]).toMatchObject({
key: {
@@ -338,12 +340,12 @@ describe('compiler: transform v-model', () => {
})
expect(root.cached).toBe(1)
const codegen = (root.children[0] as PlainElementNode)
.codegenNode as PlainElementCodegenNode
.codegenNode as VNodeCall
// should not list cached prop in dynamicProps
expect(codegen.arguments[4]).toBe(`["modelValue"]`)
expect(
(codegen.arguments[1] as ObjectExpression).properties[1].value.type
).toBe(NodeTypes.JS_CACHE_EXPRESSION)
expect(codegen.dynamicProps).toBe(`["modelValue"]`)
expect((codegen.props as ObjectExpression).properties[1].value.type).toBe(
NodeTypes.JS_CACHE_EXPRESSION
)
})
test('should not cache update handler if it refers v-for scope variables', () => {
@@ -356,10 +358,10 @@ describe('compiler: transform v-model', () => {
)
expect(root.cached).toBe(0)
const codegen = ((root.children[0] as ForNode)
.children[0] as PlainElementNode).codegenNode as PlainElementCodegenNode
expect(codegen.arguments[4]).toBe(`["modelValue", "onUpdate:modelValue"]`)
.children[0] as PlainElementNode).codegenNode as VNodeCall
expect(codegen.dynamicProps).toBe(`["modelValue", "onUpdate:modelValue"]`)
expect(
(codegen.arguments[1] as ObjectExpression).properties[1].value.type
(codegen.props as ObjectExpression).properties[1].value.type
).not.toBe(NodeTypes.JS_CACHE_EXPRESSION)
})
@@ -371,18 +373,18 @@ describe('compiler: transform v-model', () => {
}
)
const codegen = ((root.children[0] as ComponentNode)
.children[0] as PlainElementNode).codegenNode as PlainElementCodegenNode
expect(codegen.arguments[4]).toBe(`["modelValue", "onUpdate:modelValue"]`)
.children[0] as PlainElementNode).codegenNode as VNodeCall
expect(codegen.dynamicProps).toBe(`["modelValue", "onUpdate:modelValue"]`)
})
test('should generate modelModifers for component v-model', () => {
const root = parseWithVModel('<Comp v-model.trim.bar-baz="foo" />', {
prefixIdentifiers: true
})
const args = ((root.children[0] as ComponentNode)
.codegenNode as CallExpression).arguments
const vnodeCall = (root.children[0] as ComponentNode)
.codegenNode as VNodeCall
// props
expect(args[1]).toMatchObject({
expect(vnodeCall.props).toMatchObject({
properties: [
{ key: { content: `modelValue` } },
{ key: { content: `onUpdate:modelValue` } },
@@ -394,7 +396,7 @@ describe('compiler: transform v-model', () => {
})
// should NOT include modelModifiers in dynamicPropNames because it's never
// gonna change
expect(args[4]).toBe(`["modelValue", "onUpdate:modelValue"]`)
expect(vnodeCall.dynamicProps).toBe(`["modelValue", "onUpdate:modelValue"]`)
})
test('should generate modelModifers for component v-model with arguments', () => {
@@ -404,10 +406,10 @@ describe('compiler: transform v-model', () => {
prefixIdentifiers: true
}
)
const args = ((root.children[0] as ComponentNode)
.codegenNode as CallExpression).arguments
const vnodeCall = (root.children[0] as ComponentNode)
.codegenNode as VNodeCall
// props
expect(args[1]).toMatchObject({
expect(vnodeCall.props).toMatchObject({
properties: [
{ key: { content: `foo` } },
{ key: { content: `onUpdate:foo` } },
@@ -425,7 +427,9 @@ describe('compiler: transform v-model', () => {
})
// should NOT include modelModifiers in dynamicPropNames because it's never
// gonna change
expect(args[4]).toBe(`["foo", "onUpdate:foo", "bar", "onUpdate:bar"]`)
expect(vnodeCall.dynamicProps).toBe(
`["foo", "onUpdate:foo", "bar", "onUpdate:bar"]`
)
})
describe('errors', () => {

View File

@@ -1,13 +1,12 @@
import {
parse,
baseParse as parse,
transform,
ElementNode,
ObjectExpression,
CompilerOptions,
ErrorCodes,
NodeTypes,
CallExpression,
PlainElementCodegenNode
VNodeCall
} from '../../src'
import { transformOn } from '../../src/transforms/vOn'
import { transformElement } from '../../src/transforms/transformElement'
@@ -31,54 +30,58 @@ function parseWithVOn(template: string, options: CompilerOptions = {}) {
describe('compiler: transform v-on', () => {
test('basic', () => {
const { node } = parseWithVOn(`<div v-on:click="onClick"/>`)
const props = (node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: {
content: `onClick`,
isStatic: true,
loc: {
start: {
line: 1,
column: 11
expect((node.codegenNode as VNodeCall).props).toMatchObject({
properties: [
{
key: {
content: `onClick`,
isStatic: true,
loc: {
start: {
line: 1,
column: 11
},
end: {
line: 1,
column: 16
}
}
},
end: {
line: 1,
column: 16
value: {
content: `onClick`,
isStatic: false,
loc: {
start: {
line: 1,
column: 18
},
end: {
line: 1,
column: 25
}
}
}
}
},
value: {
content: `onClick`,
isStatic: false,
loc: {
start: {
line: 1,
column: 18
},
end: {
line: 1,
column: 25
}
}
}
]
})
})
test('dynamic arg', () => {
const { node } = parseWithVOn(`<div v-on:[event]="handler"/>`)
const props = (node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [`"on" + (`, { content: `event` }, `)`]
},
value: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: `handler`,
isStatic: false
}
expect((node.codegenNode as VNodeCall).props).toMatchObject({
properties: [
{
key: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [`"on" + (`, { content: `event` }, `)`]
},
value: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: `handler`,
isStatic: false
}
}
]
})
})
@@ -86,18 +89,20 @@ describe('compiler: transform v-on', () => {
const { node } = parseWithVOn(`<div v-on:[event]="handler"/>`, {
prefixIdentifiers: true
})
const props = (node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [`"on" + (`, { content: `_ctx.event` }, `)`]
},
value: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: `_ctx.handler`,
isStatic: false
}
expect((node.codegenNode as VNodeCall).props).toMatchObject({
properties: [
{
key: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [`"on" + (`, { content: `_ctx.event` }, `)`]
},
value: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: `_ctx.handler`,
isStatic: false
}
}
]
})
})
@@ -105,38 +110,60 @@ describe('compiler: transform v-on', () => {
const { node } = parseWithVOn(`<div v-on:[event(foo)]="handler"/>`, {
prefixIdentifiers: true
})
const props = (node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
`"on" + (`,
{ content: `_ctx.event` },
`(`,
{ content: `_ctx.foo` },
`)`,
`)`
]
},
value: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: `_ctx.handler`,
isStatic: false
}
expect((node.codegenNode as VNodeCall).props).toMatchObject({
properties: [
{
key: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
`"on" + (`,
{ content: `_ctx.event` },
`(`,
{ content: `_ctx.foo` },
`)`,
`)`
]
},
value: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: `_ctx.handler`,
isStatic: false
}
}
]
})
})
test('should wrap as function if expression is inline statement', () => {
const { node } = parseWithVOn(`<div @click="i++"/>`)
const props = (node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: { content: `onClick` },
value: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [`$event => (`, { content: `i++` }, `)`]
}
expect((node.codegenNode as VNodeCall).props).toMatchObject({
properties: [
{
key: { content: `onClick` },
value: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [`$event => (`, { content: `i++` }, `)`]
}
}
]
})
})
test('should handle multiple inline statement', () => {
const { node } = parseWithVOn(`<div @click="foo();bar()"/>`)
expect((node.codegenNode as VNodeCall).props).toMatchObject({
properties: [
{
key: { content: `onClick` },
value: {
type: NodeTypes.COMPOUND_EXPRESSION,
// should wrap with `{` for multiple statements
// in this case the return value is discarded and the behavior is
// consistent with 2.x
children: [`$event => {`, { content: `foo();bar()` }, `}`]
}
}
]
})
})
@@ -144,48 +171,90 @@ describe('compiler: transform v-on', () => {
const { node } = parseWithVOn(`<div @click="foo($event)"/>`, {
prefixIdentifiers: true
})
const props = (node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: { content: `onClick` },
value: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
`$event => (`,
{ content: `_ctx.foo` },
`(`,
// should NOT prefix $event
{ content: `$event` },
`)`,
`)`
]
}
expect((node.codegenNode as VNodeCall).props).toMatchObject({
properties: [
{
key: { content: `onClick` },
value: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
`$event => (`,
{
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
{ content: `_ctx.foo` },
`(`,
// should NOT prefix $event
{ content: `$event` },
`)`
]
},
`)`
]
}
}
]
})
})
test('multiple inline statements w/ prefixIdentifiers: true', () => {
const { node } = parseWithVOn(`<div @click="foo($event);bar()"/>`, {
prefixIdentifiers: true
})
expect((node.codegenNode as VNodeCall).props).toMatchObject({
properties: [
{
key: { content: `onClick` },
value: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
`$event => {`,
{
children: [
{ content: `_ctx.foo` },
`(`,
// should NOT prefix $event
{ content: `$event` },
`);`,
{ content: `_ctx.bar` },
`()`
]
},
`}`
]
}
}
]
})
})
test('should NOT wrap as function if expression is already function expression', () => {
const { node } = parseWithVOn(`<div @click="$event => foo($event)"/>`)
const props = (node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: { content: `onClick` },
value: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: `$event => foo($event)`
}
expect((node.codegenNode as VNodeCall).props).toMatchObject({
properties: [
{
key: { content: `onClick` },
value: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: `$event => foo($event)`
}
}
]
})
})
test('should NOT wrap as function if expression is complex member expression', () => {
const { node } = parseWithVOn(`<div @click="a['b' + c]"/>`)
const props = (node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: { content: `onClick` },
value: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: `a['b' + c]`
}
expect((node.codegenNode as VNodeCall).props).toMatchObject({
properties: [
{
key: { content: `onClick` },
value: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: `a['b' + c]`
}
}
]
})
})
@@ -193,14 +262,21 @@ describe('compiler: transform v-on', () => {
const { node } = parseWithVOn(`<div @click="a['b' + c]"/>`, {
prefixIdentifiers: true
})
const props = (node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: { content: `onClick` },
value: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [{ content: `_ctx.a` }, `['b' + `, { content: `_ctx.c` }, `]`]
}
expect((node.codegenNode as VNodeCall).props).toMatchObject({
properties: [
{
key: { content: `onClick` },
value: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
{ content: `_ctx.a` },
`['b' + `,
{ content: `_ctx.c` },
`]`
]
}
}
]
})
})
@@ -208,21 +284,23 @@ describe('compiler: transform v-on', () => {
const { node } = parseWithVOn(`<div @click="e => foo(e)"/>`, {
prefixIdentifiers: true
})
const props = (node.codegenNode as CallExpression)
.arguments[1] as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: { content: `onClick` },
value: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
{ content: `e` },
` => `,
{ content: `_ctx.foo` },
`(`,
{ content: `e` },
`)`
]
}
expect((node.codegenNode as VNodeCall).props).toMatchObject({
properties: [
{
key: { content: `onClick` },
value: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [
{ content: `e` },
` => `,
{ content: `_ctx.foo` },
`(`,
{ content: `e` },
`)`
]
}
}
]
})
})
@@ -250,6 +328,22 @@ describe('compiler: transform v-on', () => {
expect(onError).not.toHaveBeenCalled()
})
test('case conversion for vnode hooks', () => {
const { node } = parseWithVOn(`<div v-on:vnode-mounted="onMount"/>`)
expect((node.codegenNode as VNodeCall).props).toMatchObject({
properties: [
{
key: {
content: `onVnodeMounted`
},
value: {
content: `onMount`
}
}
]
})
})
describe('cacheHandler', () => {
test('empty handler', () => {
const { root, node } = parseWithVOn(`<div v-on:click.prevent />`, {
@@ -257,10 +351,12 @@ describe('compiler: transform v-on', () => {
cacheHandlers: true
})
expect(root.cached).toBe(1)
const args = (node.codegenNode as PlainElementCodegenNode).arguments
const vnodeCall = node.codegenNode as VNodeCall
// should not treat cached handler as dynamicProp, so no flags
expect(args.length).toBe(2)
expect((args[1] as ObjectExpression).properties[0].value).toMatchObject({
expect(vnodeCall.patchFlag).toBeUndefined()
expect(
(vnodeCall.props as ObjectExpression).properties[0].value
).toMatchObject({
type: NodeTypes.JS_CACHE_EXPRESSION,
index: 1,
value: {
@@ -276,10 +372,12 @@ describe('compiler: transform v-on', () => {
cacheHandlers: true
})
expect(root.cached).toBe(1)
const args = (node.codegenNode as PlainElementCodegenNode).arguments
const vnodeCall = node.codegenNode as VNodeCall
// should not treat cached handler as dynamicProp, so no flags
expect(args.length).toBe(2)
expect((args[1] as ObjectExpression).properties[0].value).toMatchObject({
expect(vnodeCall.patchFlag).toBeUndefined()
expect(
(vnodeCall.props as ObjectExpression).properties[0].value
).toMatchObject({
type: NodeTypes.JS_CACHE_EXPRESSION,
index: 1,
value: {
@@ -295,10 +393,12 @@ describe('compiler: transform v-on', () => {
cacheHandlers: true
})
expect(root.cached).toBe(1)
const args = (node.codegenNode as PlainElementCodegenNode).arguments
const vnodeCall = node.codegenNode as VNodeCall
// should not treat cached handler as dynamicProp, so no flags
expect(args.length).toBe(2)
expect((args[1] as ObjectExpression).properties[0].value).toMatchObject({
expect(vnodeCall.patchFlag).toBeUndefined()
expect(
(vnodeCall.props as ObjectExpression).properties[0].value
).toMatchObject({
type: NodeTypes.JS_CACHE_EXPRESSION,
index: 1,
value: {
@@ -314,15 +414,22 @@ describe('compiler: transform v-on', () => {
cacheHandlers: true
})
expect(root.cached).toBe(1)
const args = (node.codegenNode as PlainElementCodegenNode).arguments
expect(root.cached).toBe(1)
const vnodeCall = node.codegenNode as VNodeCall
// should not treat cached handler as dynamicProp, so no flags
expect(args.length).toBe(2)
expect((args[1] as ObjectExpression).properties[0].value).toMatchObject({
expect(vnodeCall.patchFlag).toBeUndefined()
expect(
(vnodeCall.props as ObjectExpression).properties[0].value
).toMatchObject({
type: NodeTypes.JS_CACHE_EXPRESSION,
index: 1,
value: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [`$event => (`, { content: `_ctx.foo` }, `++`, `)`]
children: [
`$event => (`,
{ children: [{ content: `_ctx.foo` }, `++`] },
`)`
]
}
})
})

View File

@@ -1,5 +1,5 @@
import {
parse,
baseParse as parse,
transform,
NodeTypes,
generate,
@@ -7,11 +7,7 @@ import {
} from '../../src'
import { transformOnce } from '../../src/transforms/vOnce'
import { transformElement } from '../../src/transforms/transformElement'
import {
CREATE_VNODE,
RENDER_SLOT,
SET_BLOCK_TRACKING
} from '../../src/runtimeHelpers'
import { RENDER_SLOT, SET_BLOCK_TRACKING } from '../../src/runtimeHelpers'
import { transformBind } from '../../src/transforms/vBind'
import { transformSlotOutlet } from '../../src/transforms/transformSlotOutlet'
@@ -36,8 +32,8 @@ describe('compiler: v-once transform', () => {
type: NodeTypes.JS_CACHE_EXPRESSION,
index: 1,
value: {
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_VNODE
type: NodeTypes.VNODE_CALL,
tag: `"div"`
}
})
expect(generate(root).code).toMatchSnapshot()
@@ -51,8 +47,8 @@ describe('compiler: v-once transform', () => {
type: NodeTypes.JS_CACHE_EXPRESSION,
index: 1,
value: {
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_VNODE
type: NodeTypes.VNODE_CALL,
tag: `"div"`
}
})
expect(generate(root).code).toMatchSnapshot()
@@ -66,8 +62,8 @@ describe('compiler: v-once transform', () => {
type: NodeTypes.JS_CACHE_EXPRESSION,
index: 1,
value: {
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_VNODE
type: NodeTypes.VNODE_CALL,
tag: `_component_Comp`
}
})
expect(generate(root).code).toMatchSnapshot()
@@ -100,8 +96,8 @@ describe('compiler: v-once transform', () => {
type: NodeTypes.JS_CACHE_EXPRESSION,
index: 1,
value: {
type: NodeTypes.JS_CALL_EXPRESSION,
callee: CREATE_VNODE
type: NodeTypes.VNODE_CALL,
tag: `"div"`
}
})
expect(generate(root).code).toMatchSnapshot()

View File

@@ -1,14 +1,15 @@
import {
CompilerOptions,
parse,
baseParse as parse,
transform,
generate,
ElementNode,
NodeTypes,
ErrorCodes,
ForNode,
CallExpression,
ComponentNode
ComponentNode,
VNodeCall,
SlotsExpression
} from '../../src'
import { transformElement } from '../../src/transforms/transformElement'
import { transformOn } from '../../src/transforms/vOn'
@@ -46,7 +47,8 @@ function parseWithSlots(template: string, options: CompilerOptions = {}) {
root: ast,
slots:
ast.children[0].type === NodeTypes.ELEMENT
? (ast.children[0].codegenNode as CallExpression).arguments[2]
? ((ast.children[0].codegenNode as VNodeCall)
.children as SlotsExpression)
: null
}
}
@@ -67,8 +69,8 @@ function createSlotMatcher(obj: Record<string, any>) {
} as any
})
.concat({
key: { content: `_compiled` },
value: { content: `true` }
key: { content: `_` },
value: { content: `1`, isStatic: false }
})
}
}
@@ -95,7 +97,7 @@ describe('compiler: transform component slots', () => {
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
})
test('explicit default slot', () => {
test('on-component default slot', () => {
const { root, slots } = parseWithSlots(
`<Comp v-slot="{ foo }">{{ foo }}{{ bar }}</Comp>`,
{ prefixIdentifiers: true }
@@ -128,7 +130,40 @@ describe('compiler: transform component slots', () => {
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
})
test('named slots', () => {
test('on component named slot', () => {
const { root, slots } = parseWithSlots(
`<Comp v-slot:named="{ foo }">{{ foo }}{{ bar }}</Comp>`,
{ prefixIdentifiers: true }
)
expect(slots).toMatchObject(
createSlotMatcher({
named: {
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [`{ `, { content: `foo` }, ` }`]
},
returns: [
{
type: NodeTypes.INTERPOLATION,
content: {
content: `foo`
}
},
{
type: NodeTypes.INTERPOLATION,
content: {
content: `_ctx.bar`
}
}
]
}
})
)
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
})
test('template named slots', () => {
const { root, slots } = parseWithSlots(
`<Comp>
<template v-slot:one="{ foo }">
@@ -189,6 +224,76 @@ describe('compiler: transform component slots', () => {
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
})
test('on component dynamically named slot', () => {
const { root, slots } = parseWithSlots(
`<Comp v-slot:[named]="{ foo }">{{ foo }}{{ bar }}</Comp>`,
{ prefixIdentifiers: true }
)
expect(slots).toMatchObject(
createSlotMatcher({
'[_ctx.named]': {
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [`{ `, { content: `foo` }, ` }`]
},
returns: [
{
type: NodeTypes.INTERPOLATION,
content: {
content: `foo`
}
},
{
type: NodeTypes.INTERPOLATION,
content: {
content: `_ctx.bar`
}
}
]
}
})
)
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
})
test('named slots w/ implicit default slot', () => {
const { root, slots } = parseWithSlots(
`<Comp>
<template #one>foo</template>bar<span/>
</Comp>`
)
expect(slots).toMatchObject(
createSlotMatcher({
one: {
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: undefined,
returns: [
{
type: NodeTypes.TEXT,
content: `foo`
}
]
},
default: {
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: undefined,
returns: [
{
type: NodeTypes.TEXT,
content: `bar`
},
{
type: NodeTypes.ELEMENT,
tag: `span`
}
]
}
})
)
expect(generate(root).code).toMatchSnapshot()
})
test('dynamically named slots', () => {
const { root, slots } = parseWithSlots(
`<Comp>
@@ -274,43 +379,41 @@ describe('compiler: transform component slots', () => {
{
type: NodeTypes.ELEMENT,
codegenNode: {
type: NodeTypes.JS_CALL_EXPRESSION,
arguments: [
`_component_Inner`,
`null`,
createSlotMatcher({
default: {
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [`{ `, { content: `bar` }, ` }`]
},
returns: [
{
type: NodeTypes.INTERPOLATION,
content: {
content: `foo`
}
},
{
type: NodeTypes.INTERPOLATION,
content: {
content: `bar`
}
},
{
type: NodeTypes.INTERPOLATION,
content: {
content: `_ctx.baz`
}
type: NodeTypes.VNODE_CALL,
tag: `_component_Inner`,
props: undefined,
children: createSlotMatcher({
default: {
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [`{ `, { content: `bar` }, ` }`]
},
returns: [
{
type: NodeTypes.INTERPOLATION,
content: {
content: `foo`
}
]
}
}),
// nested slot should be forced dynamic, since scope variables
// are not tracked as dependencies of the slot.
genFlagText(PatchFlags.DYNAMIC_SLOTS)
]
},
{
type: NodeTypes.INTERPOLATION,
content: {
content: `bar`
}
},
{
type: NodeTypes.INTERPOLATION,
content: {
content: `_ctx.baz`
}
}
]
}
}),
// nested slot should be forced dynamic, since scope variables
// are not tracked as dependencies of the slot.
patchFlag: genFlagText(PatchFlags.DYNAMIC_SLOTS)
}
},
// test scope
@@ -351,8 +454,8 @@ describe('compiler: transform component slots', () => {
)
const div = ((root.children[0] as ForNode).children[0] as ElementNode)
.codegenNode as any
const comp = div.arguments[2][0]
expect(comp.codegenNode.arguments[3]).toBe(
const comp = div.children[0]
expect(comp.codegenNode.patchFlag).toBe(
genFlagText(PatchFlags.DYNAMIC_SLOTS)
)
})
@@ -364,12 +467,12 @@ describe('compiler: transform component slots', () => {
if (root.children[0].type === NodeTypes.FOR) {
const div = (root.children[0].children[0] as ElementNode)
.codegenNode as any
const comp = div.arguments[2][0]
flag = comp.codegenNode.arguments[3]
const comp = div.children[0]
flag = comp.codegenNode.patchFlag
} else {
const innerComp = (root.children[0] as ComponentNode)
.children[0] as ComponentNode
flag = (innerComp.codegenNode as CallExpression).arguments[3]
flag = (innerComp.codegenNode as VNodeCall).patchFlag
}
if (shouldForce) {
expect(flag).toBe(genFlagText(PatchFlags.DYNAMIC_SLOTS))
@@ -419,7 +522,7 @@ describe('compiler: transform component slots', () => {
callee: CREATE_SLOTS,
arguments: [
createObjectMatcher({
_compiled: `[true]`
_: `[1]`
}),
{
type: NodeTypes.JS_ARRAY_EXPRESSION,
@@ -443,7 +546,7 @@ describe('compiler: transform component slots', () => {
}
]
})
expect((root as any).children[0].codegenNode.arguments[3]).toMatch(
expect((root as any).children[0].codegenNode.patchFlag).toMatch(
PatchFlags.DYNAMIC_SLOTS + ''
)
expect(generate(root).code).toMatchSnapshot()
@@ -461,7 +564,7 @@ describe('compiler: transform component slots', () => {
callee: CREATE_SLOTS,
arguments: [
createObjectMatcher({
_compiled: `[true]`
_: `[1]`
}),
{
type: NodeTypes.JS_ARRAY_EXPRESSION,
@@ -491,7 +594,7 @@ describe('compiler: transform component slots', () => {
}
]
})
expect((root as any).children[0].codegenNode.arguments[3]).toMatch(
expect((root as any).children[0].codegenNode.patchFlag).toMatch(
PatchFlags.DYNAMIC_SLOTS + ''
)
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
@@ -510,7 +613,7 @@ describe('compiler: transform component slots', () => {
callee: CREATE_SLOTS,
arguments: [
createObjectMatcher({
_compiled: `[true]`
_: `[1]`
}),
{
type: NodeTypes.JS_ARRAY_EXPRESSION,
@@ -551,7 +654,7 @@ describe('compiler: transform component slots', () => {
}
]
})
expect((root as any).children[0].codegenNode.arguments[3]).toMatch(
expect((root as any).children[0].codegenNode.patchFlag).toMatch(
PatchFlags.DYNAMIC_SLOTS + ''
)
expect(generate(root).code).toMatchSnapshot()
@@ -569,7 +672,7 @@ describe('compiler: transform component slots', () => {
callee: CREATE_SLOTS,
arguments: [
createObjectMatcher({
_compiled: `[true]`
_: `[1]`
}),
{
type: NodeTypes.JS_ARRAY_EXPRESSION,
@@ -601,20 +704,20 @@ describe('compiler: transform component slots', () => {
}
]
})
expect((root as any).children[0].codegenNode.arguments[3]).toMatch(
expect((root as any).children[0].codegenNode.patchFlag).toMatch(
PatchFlags.DYNAMIC_SLOTS + ''
)
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
})
describe('errors', () => {
test('error on extraneous children w/ named slots', () => {
test('error on extraneous children w/ named default slot', () => {
const onError = jest.fn()
const source = `<Comp><template #default>foo</template>bar</Comp>`
parseWithSlots(source, { onError })
const index = source.indexOf('bar')
expect(onError.mock.calls[0][0]).toMatchObject({
code: ErrorCodes.X_V_SLOT_EXTRANEOUS_NON_SLOT_CHILDREN,
code: ErrorCodes.X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN,
loc: {
source: `bar`,
start: {
@@ -699,28 +802,5 @@ describe('compiler: transform component slots', () => {
}
})
})
test('error on named slot on component', () => {
const onError = jest.fn()
const source = `<Comp v-slot:foo>foo</Comp>`
parseWithSlots(source, { onError })
const index = source.indexOf('v-slot')
expect(onError.mock.calls[0][0]).toMatchObject({
code: ErrorCodes.X_V_SLOT_NAMED_SLOT_ON_COMPONENT,
loc: {
source: `v-slot:foo`,
start: {
offset: index,
line: 1,
column: index + 1
},
end: {
offset: index + 10,
line: 1,
column: index + 11
}
}
})
})
})
})