diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap
index 32a6fd611..4ee25d5ac 100644
--- a/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap
+++ b/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap
@@ -5,14 +5,15 @@ exports[`compiler: hoistStatic transform hoist element with static key 1`] = `
const { createElementVNode: _createElementVNode } = _Vue
const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"div\\", { key: \\"foo\\" }, null, -1 /* HOISTED */)
+const _hoisted_2 = [
+ _hoisted_1
+]
return function render(_ctx, _cache) {
with (_ctx) {
const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
- return (_openBlock(), _createElementBlock(\\"div\\", null, [
- _hoisted_1
- ]))
+ return (_openBlock(), _createElementBlock(\\"div\\", null, _hoisted_2))
}
}"
`;
@@ -25,14 +26,15 @@ const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"p\\", null, [
/*#__PURE__*/_createElementVNode(\\"span\\"),
/*#__PURE__*/_createElementVNode(\\"span\\")
], -1 /* HOISTED */)
+const _hoisted_2 = [
+ _hoisted_1
+]
return function render(_ctx, _cache) {
with (_ctx) {
const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
- return (_openBlock(), _createElementBlock(\\"div\\", null, [
- _hoisted_1
- ]))
+ return (_openBlock(), _createElementBlock(\\"div\\", null, _hoisted_2))
}
}"
`;
@@ -44,14 +46,15 @@ const { createElementVNode: _createElementVNode, createCommentVNode: _createComm
const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"div\\", null, [
/*#__PURE__*/_createCommentVNode(\\"comment\\")
], -1 /* HOISTED */)
+const _hoisted_2 = [
+ _hoisted_1
+]
return function render(_ctx, _cache) {
with (_ctx) {
const { createCommentVNode: _createCommentVNode, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
- return (_openBlock(), _createElementBlock(\\"div\\", null, [
- _hoisted_1
- ]))
+ return (_openBlock(), _createElementBlock(\\"div\\", null, _hoisted_2))
}
}"
`;
@@ -62,15 +65,16 @@ const { createElementVNode: _createElementVNode } = _Vue
const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"span\\", null, null, -1 /* HOISTED */)
const _hoisted_2 = /*#__PURE__*/_createElementVNode(\\"div\\", null, null, -1 /* HOISTED */)
+const _hoisted_3 = [
+ _hoisted_1,
+ _hoisted_2
+]
return function render(_ctx, _cache) {
with (_ctx) {
const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
- return (_openBlock(), _createElementBlock(\\"div\\", null, [
- _hoisted_1,
- _hoisted_2
- ]))
+ return (_openBlock(), _createElementBlock(\\"div\\", null, _hoisted_3))
}
}"
`;
@@ -80,14 +84,15 @@ exports[`compiler: hoistStatic transform hoist simple element 1`] = `
const { createElementVNode: _createElementVNode } = _Vue
const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"span\\", { class: \\"inline\\" }, \\"hello\\", -1 /* HOISTED */)
+const _hoisted_2 = [
+ _hoisted_1
+]
return function render(_ctx, _cache) {
with (_ctx) {
const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
- return (_openBlock(), _createElementBlock(\\"div\\", null, [
- _hoisted_1
- ]))
+ return (_openBlock(), _createElementBlock(\\"div\\", null, _hoisted_2))
}
}"
`;
@@ -175,14 +180,15 @@ exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static t
const { createElementVNode: _createElementVNode } = _Vue
const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"span\\", null, \\"foo \\" + /*#__PURE__*/_toDisplayString(1) + \\" \\" + /*#__PURE__*/_toDisplayString(true), -1 /* HOISTED */)
+const _hoisted_2 = [
+ _hoisted_1
+]
return function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
- return (_openBlock(), _createElementBlock(\\"div\\", null, [
- _hoisted_1
- ]))
+ return (_openBlock(), _createElementBlock(\\"div\\", null, _hoisted_2))
}
}"
`;
@@ -192,14 +198,15 @@ exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static t
const { createElementVNode: _createElementVNode } = _Vue
const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"span\\", { foo: 0 }, /*#__PURE__*/_toDisplayString(1), -1 /* HOISTED */)
+const _hoisted_2 = [
+ _hoisted_1
+]
return function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
- return (_openBlock(), _createElementBlock(\\"div\\", null, [
- _hoisted_1
- ]))
+ return (_openBlock(), _createElementBlock(\\"div\\", null, _hoisted_2))
}
}"
`;
@@ -368,6 +375,9 @@ const { createElementVNode: _createElementVNode } = _Vue
const _hoisted_1 = { id: \\"foo\\" }
const _hoisted_2 = /*#__PURE__*/_createElementVNode(\\"span\\", null, null, -1 /* HOISTED */)
+const _hoisted_3 = [
+ _hoisted_2
+]
return function render(_ctx, _cache) {
with (_ctx) {
@@ -375,9 +385,7 @@ return function render(_ctx, _cache) {
return (_openBlock(), _createElementBlock(\\"div\\", null, [
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(list, (i) => {
- return (_openBlock(), _createElementBlock(\\"div\\", _hoisted_1, [
- _hoisted_2
- ]))
+ return (_openBlock(), _createElementBlock(\\"div\\", _hoisted_1, _hoisted_3))
}), 256 /* UNKEYED_FRAGMENT */))
]))
}
@@ -393,6 +401,9 @@ const _hoisted_1 = {
id: \\"foo\\"
}
const _hoisted_2 = /*#__PURE__*/_createElementVNode(\\"span\\", null, null, -1 /* HOISTED */)
+const _hoisted_3 = [
+ _hoisted_2
+]
return function render(_ctx, _cache) {
with (_ctx) {
@@ -400,9 +411,7 @@ return function render(_ctx, _cache) {
return (_openBlock(), _createElementBlock(\\"div\\", null, [
ok
- ? (_openBlock(), _createElementBlock(\\"div\\", _hoisted_1, [
- _hoisted_2
- ]))
+ ? (_openBlock(), _createElementBlock(\\"div\\", _hoisted_1, _hoisted_3))
: _createCommentVNode(\\"v-if\\", true)
]))
}
diff --git a/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts b/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts
index b2e125afd..04363431b 100644
--- a/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts
+++ b/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts
@@ -26,6 +26,17 @@ import { createObjectMatcher, genFlagText } from '../testUtils'
import { transformText } from '../../src/transforms/transformText'
import { PatchFlags } from '@vue/shared'
+const hoistedChildrenArrayMatcher = (startIndex = 1, length = 1) => ({
+ type: NodeTypes.JS_ARRAY_EXPRESSION,
+ elements: new Array(length).fill(0).map((_, i) => ({
+ type: NodeTypes.ELEMENT,
+ codegenNode: {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: `_hoisted_${startIndex + i}`
+ }
+ }))
+})
+
function transformWithHoist(template: string, options: CompilerOptions = {}) {
const ast = parse(template)
transform(ast, {
@@ -75,20 +86,13 @@ describe('compiler: hoistStatic transform', () => {
type: NodeTypes.TEXT,
content: `hello`
}
- }
+ },
+ hoistedChildrenArrayMatcher()
])
expect(root.codegenNode).toMatchObject({
tag: `"div"`,
props: undefined,
- children: [
- {
- type: NodeTypes.ELEMENT,
- codegenNode: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `_hoisted_1`
- }
- }
- ]
+ children: { content: `_hoisted_2` }
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -104,17 +108,12 @@ describe('compiler: hoistStatic transform', () => {
{ type: NodeTypes.ELEMENT, tag: `span` },
{ type: NodeTypes.ELEMENT, tag: `span` }
]
- }
- ])
- expect((root.codegenNode as VNodeCall).children).toMatchObject([
- {
- type: NodeTypes.ELEMENT,
- codegenNode: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `_hoisted_1`
- }
- }
+ },
+ hoistedChildrenArrayMatcher()
])
+ expect((root.codegenNode as VNodeCall).children).toMatchObject({
+ content: '_hoisted_2'
+ })
expect(generate(root).code).toMatchSnapshot()
})
@@ -126,17 +125,12 @@ describe('compiler: hoistStatic transform', () => {
tag: `"div"`,
props: undefined,
children: [{ type: NodeTypes.COMMENT, content: `comment` }]
- }
- ])
- expect((root.codegenNode as VNodeCall).children).toMatchObject([
- {
- type: NodeTypes.ELEMENT,
- codegenNode: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `_hoisted_1`
- }
- }
+ },
+ hoistedChildrenArrayMatcher()
])
+ expect((root.codegenNode as VNodeCall).children).toMatchObject({
+ content: `_hoisted_2`
+ })
expect(generate(root).code).toMatchSnapshot()
})
@@ -150,24 +144,12 @@ describe('compiler: hoistStatic transform', () => {
{
type: NodeTypes.VNODE_CALL,
tag: `"div"`
- }
- ])
- expect((root.codegenNode as VNodeCall).children).toMatchObject([
- {
- type: NodeTypes.ELEMENT,
- codegenNode: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `_hoisted_1`
- }
},
- {
- type: NodeTypes.ELEMENT,
- codegenNode: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `_hoisted_2`
- }
- }
+ hoistedChildrenArrayMatcher(1, 2)
])
+ expect((root.codegenNode as VNodeCall).children).toMatchObject({
+ content: '_hoisted_3'
+ })
expect(generate(root).code).toMatchSnapshot()
})
@@ -213,26 +195,19 @@ describe('compiler: hoistStatic transform', () => {
test('hoist element with static key', () => {
const root = transformWithHoist(`
`)
- expect(root.hoists.length).toBe(1)
+ expect(root.hoists.length).toBe(2)
expect(root.hoists).toMatchObject([
{
type: NodeTypes.VNODE_CALL,
tag: `"div"`,
props: createObjectMatcher({ key: 'foo' })
- }
+ },
+ hoistedChildrenArrayMatcher()
])
expect(root.codegenNode).toMatchObject({
tag: `"div"`,
props: undefined,
- children: [
- {
- type: NodeTypes.ELEMENT,
- codegenNode: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `_hoisted_1`
- }
- }
- ]
+ children: { content: `_hoisted_2` }
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -348,7 +323,8 @@ describe('compiler: hoistStatic transform', () => {
{
type: NodeTypes.VNODE_CALL,
tag: `"span"`
- }
+ },
+ hoistedChildrenArrayMatcher(2)
])
expect(
((root.children[0] as ElementNode).children[0] as IfNode).codegenNode
@@ -359,11 +335,7 @@ describe('compiler: hoistStatic transform', () => {
type: NodeTypes.VNODE_CALL,
tag: `"div"`,
props: { content: `_hoisted_1` },
- children: [
- {
- codegenNode: { content: `_hoisted_2` }
- }
- ]
+ children: { content: `_hoisted_3` }
}
})
expect(generate(root).code).toMatchSnapshot()
@@ -380,7 +352,8 @@ describe('compiler: hoistStatic transform', () => {
{
type: NodeTypes.VNODE_CALL,
tag: `"span"`
- }
+ },
+ hoistedChildrenArrayMatcher(2)
])
const forBlockCodegen = ((root.children[0] as ElementNode)
.children[0] as ForNode).codegenNode
@@ -399,11 +372,7 @@ describe('compiler: hoistStatic transform', () => {
type: NodeTypes.VNODE_CALL,
tag: `"div"`,
props: { content: `_hoisted_1` },
- children: [
- {
- codegenNode: { content: `_hoisted_2` }
- }
- ]
+ children: { content: `_hoisted_3` }
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -423,6 +392,17 @@ describe('compiler: hoistStatic transform', () => {
{
type: NodeTypes.VNODE_CALL,
tag: `"div"`
+ },
+ {
+ type: NodeTypes.JS_ARRAY_EXPRESSION,
+ elements: [
+ {
+ type: NodeTypes.TEXT_CALL
+ },
+ {
+ type: NodeTypes.ELEMENT
+ }
+ ]
}
])
})
@@ -443,20 +423,16 @@ describe('compiler: hoistStatic transform', () => {
children: {
type: NodeTypes.COMPOUND_EXPRESSION
}
- }
+ },
+ hoistedChildrenArrayMatcher()
])
expect(root.codegenNode).toMatchObject({
tag: `"div"`,
props: undefined,
- children: [
- {
- type: NodeTypes.ELEMENT,
- codegenNode: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `_hoisted_1`
- }
- }
- ]
+ children: {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: `_hoisted_2`
+ }
})
expect(generate(root).code).toMatchSnapshot()
})
@@ -482,20 +458,16 @@ describe('compiler: hoistStatic transform', () => {
constType: ConstantTypes.CAN_STRINGIFY
}
}
- }
+ },
+ hoistedChildrenArrayMatcher()
])
expect(root.codegenNode).toMatchObject({
tag: `"div"`,
props: undefined,
- children: [
- {
- type: NodeTypes.ELEMENT,
- codegenNode: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `_hoisted_1`
- }
- }
- ]
+ children: {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: `_hoisted_2`
+ }
})
expect(generate(root).code).toMatchSnapshot()
})
diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts
index 2cec62d73..eaf48666b 100644
--- a/packages/compiler-core/src/ast.ts
+++ b/packages/compiler-core/src/ast.ts
@@ -286,6 +286,7 @@ export interface VNodeCall extends Node {
| TemplateTextChildNode // single text child
| SlotsExpression // component slots
| ForRenderListExpression // v-for fragment call
+ | SimpleExpressionNode // hoisted
| undefined
patchFlag: string | undefined
dynamicProps: string | SimpleExpressionNode | undefined
@@ -338,7 +339,7 @@ export interface Property extends Node {
export interface ArrayExpression extends Node {
type: NodeTypes.JS_ARRAY_EXPRESSION
- elements: Array
+ elements: Array
}
export interface FunctionExpression extends Node {
diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts
index 52f2ead4c..d930c36cd 100644
--- a/packages/compiler-core/src/codegen.ts
+++ b/packages/compiler-core/src/codegen.ts
@@ -840,7 +840,7 @@ function genObjectExpression(node: ObjectExpression, context: CodegenContext) {
}
function genArrayExpression(node: ArrayExpression, context: CodegenContext) {
- genNodeListAsArray(node.elements, context)
+ genNodeListAsArray(node.elements as CodegenNode[], context)
}
function genFunctionExpression(
diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts
index bc03d3513..44e06fc22 100644
--- a/packages/compiler-core/src/transform.ts
+++ b/packages/compiler-core/src/transform.ts
@@ -16,7 +16,8 @@ import {
createCacheExpression,
TemplateLiteral,
createVNodeCall,
- ConstantTypes
+ ConstantTypes,
+ ArrayExpression
} from './ast'
import {
isString,
@@ -113,7 +114,7 @@ export interface TransformContext
onNodeRemoved(): void
addIdentifiers(exp: ExpressionNode | string): void
removeIdentifiers(exp: ExpressionNode | string): void
- hoist(exp: string | JSChildNode): SimpleExpressionNode
+ hoist(exp: string | JSChildNode | ArrayExpression): SimpleExpressionNode
cache(exp: T, isVNode?: boolean): CacheExpression | T
constantCache: Map
diff --git a/packages/compiler-core/src/transforms/hoistStatic.ts b/packages/compiler-core/src/transforms/hoistStatic.ts
index ee18b550f..1ea895ace 100644
--- a/packages/compiler-core/src/transforms/hoistStatic.ts
+++ b/packages/compiler-core/src/transforms/hoistStatic.ts
@@ -11,10 +11,11 @@ import {
VNodeCall,
ParentNode,
JSChildNode,
- CallExpression
+ CallExpression,
+ createArrayExpression
} from '../ast'
import { TransformContext } from '../transform'
-import { PatchFlags, isString, isSymbol } from '@vue/shared'
+import { PatchFlags, isString, isSymbol, isArray } from '@vue/shared'
import { getVNodeBlockHelper, getVNodeHelper, isSlotOutlet } from '../utils'
import {
OPEN_BLOCK,
@@ -51,7 +52,6 @@ function walk(
context: TransformContext,
doNotHoistNode: boolean = false
) {
- let hasHoistedNode = false
// Some transforms, e.g. transformAssetUrls from @vue/compiler-sfc, replaces
// static bindings with expressions. These expressions are guaranteed to be
// constant so they are still eligible for hoisting, but they are only
@@ -63,6 +63,9 @@ function walk(
let canStringify = true
const { children } = node
+ const originalCount = children.length
+ let hoistedCount = 0
+
for (let i = 0; i < children.length; i++) {
const child = children[i]
// only plain elements & text calls are eligible for hoisting.
@@ -81,7 +84,7 @@ function walk(
;(child.codegenNode as VNodeCall).patchFlag =
PatchFlags.HOISTED + (__DEV__ ? ` /* HOISTED */` : ``)
child.codegenNode = context.hoist(child.codegenNode!)
- hasHoistedNode = true
+ hoistedCount++
continue
}
} else {
@@ -115,7 +118,7 @@ function walk(
}
if (contentType >= ConstantTypes.CAN_HOIST) {
child.codegenNode = context.hoist(child.codegenNode)
- hasHoistedNode = true
+ hoistedCount++
}
}
}
@@ -145,9 +148,24 @@ function walk(
}
}
- if (canStringify && hasHoistedNode && context.transformHoist) {
+ if (canStringify && hoistedCount && context.transformHoist) {
context.transformHoist(children, context, node)
}
+
+ // all children were hoisted - the entire children array is hoistable.
+ if (
+ hoistedCount &&
+ hoistedCount === originalCount &&
+ node.type === NodeTypes.ELEMENT &&
+ node.tagType === ElementTypes.ELEMENT &&
+ node.codegenNode &&
+ node.codegenNode.type === NodeTypes.VNODE_CALL &&
+ isArray(node.codegenNode.children)
+ ) {
+ node.codegenNode.children = context.hoist(
+ createArrayExpression(node.codegenNode.children)
+ )
+ }
}
export function getConstantType(
diff --git a/packages/compiler-dom/__tests__/transforms/stringifyStatic.spec.ts b/packages/compiler-dom/__tests__/transforms/stringifyStatic.spec.ts
index 612f4f013..b9e7d8120 100644
--- a/packages/compiler-dom/__tests__/transforms/stringifyStatic.spec.ts
+++ b/packages/compiler-dom/__tests__/transforms/stringifyStatic.spec.ts
@@ -30,7 +30,6 @@ describe('stringify static html', () => {
const { ast } = compileWithStringify(
``
)
- expect(ast.hoists.length).toBe(1)
// should be a normal vnode call
expect(ast.hoists[0]!.type).toBe(NodeTypes.VNODE_CALL)
})
@@ -42,21 +41,25 @@ describe('stringify static html', () => {
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
)}`
)
- expect(ast.hoists.length).toBe(1)
// should be optimized now
- expect(ast.hoists[0]).toMatchObject({
- type: NodeTypes.JS_CALL_EXPRESSION,
- callee: CREATE_STATIC,
- arguments: [
- JSON.stringify(
- `${repeat(
- ``,
- StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
- )}
`
- ),
- '1'
- ]
- })
+ expect(ast.hoists).toMatchObject([
+ {
+ type: NodeTypes.JS_CALL_EXPRESSION,
+ callee: CREATE_STATIC,
+ arguments: [
+ JSON.stringify(
+ `${repeat(
+ ``,
+ StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
+ )}
`
+ ),
+ '1'
+ ]
+ }, // the children array is hoisted as well
+ {
+ type: NodeTypes.JS_ARRAY_EXPRESSION
+ }
+ ])
})
test('should work on eligible content (elements > 20)', () => {
@@ -66,21 +69,26 @@ describe('stringify static html', () => {
StringifyThresholds.NODE_COUNT
)}`
)
- expect(ast.hoists.length).toBe(1)
// should be optimized now
- expect(ast.hoists[0]).toMatchObject({
- type: NodeTypes.JS_CALL_EXPRESSION,
- callee: CREATE_STATIC,
- arguments: [
- JSON.stringify(
- `${repeat(
- ``,
- StringifyThresholds.NODE_COUNT
- )}
`
- ),
- '1'
- ]
- })
+ expect(ast.hoists).toMatchObject([
+ {
+ type: NodeTypes.JS_CALL_EXPRESSION,
+ callee: CREATE_STATIC,
+ arguments: [
+ JSON.stringify(
+ `${repeat(
+ ``,
+ StringifyThresholds.NODE_COUNT
+ )}
`
+ ),
+ '1'
+ ]
+ },
+ // the children array is hoisted as well
+ {
+ type: NodeTypes.JS_ARRAY_EXPRESSION
+ }
+ ])
})
test('should work for multiple adjacent nodes', () => {
@@ -90,25 +98,30 @@ describe('stringify static html', () => {
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
)}`
)
- // should have 5 hoisted nodes, but the other 4 should be null
- expect(ast.hoists.length).toBe(5)
- for (let i = 1; i < 5; i++) {
- expect(ast.hoists[i]).toBe(null)
- }
- // should be optimized now
- expect(ast.hoists[0]).toMatchObject({
- type: NodeTypes.JS_CALL_EXPRESSION,
- callee: CREATE_STATIC,
- arguments: [
- JSON.stringify(
- repeat(
- ``,
- StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
- )
- ),
- '5'
- ]
- })
+ // should have 6 hoisted nodes (including the entire array),
+ // but 2~5 should be null because they are merged into 1
+ expect(ast.hoists).toMatchObject([
+ {
+ type: NodeTypes.JS_CALL_EXPRESSION,
+ callee: CREATE_STATIC,
+ arguments: [
+ JSON.stringify(
+ repeat(
+ ``,
+ StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
+ )
+ ),
+ '5'
+ ]
+ },
+ null,
+ null,
+ null,
+ null,
+ {
+ type: NodeTypes.JS_ARRAY_EXPRESSION
+ }
+ ])
})
test('serializing constant bindings', () => {
@@ -118,21 +131,25 @@ describe('stringify static html', () => {
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
)}`
)
- expect(ast.hoists.length).toBe(1)
// should be optimized now
- expect(ast.hoists[0]).toMatchObject({
- type: NodeTypes.JS_CALL_EXPRESSION,
- callee: CREATE_STATIC,
- arguments: [
- JSON.stringify(
- `${repeat(
- `1 + false`,
- StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
- )}
`
- ),
- '1'
- ]
- })
+ expect(ast.hoists).toMatchObject([
+ {
+ type: NodeTypes.JS_CALL_EXPRESSION,
+ callee: CREATE_STATIC,
+ arguments: [
+ JSON.stringify(
+ `${repeat(
+ `1 + false`,
+ StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
+ )}
`
+ ),
+ '1'
+ ]
+ },
+ {
+ type: NodeTypes.JS_ARRAY_EXPRESSION
+ }
+ ])
})
test('escape', () => {
@@ -143,21 +160,25 @@ describe('stringify static html', () => {
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
)}`
)
- expect(ast.hoists.length).toBe(1)
// should be optimized now
- expect(ast.hoists[0]).toMatchObject({
- type: NodeTypes.JS_CALL_EXPRESSION,
- callee: CREATE_STATIC,
- arguments: [
- JSON.stringify(
- `${repeat(
- `1 + <` + `&`,
- StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
- )}
`
- ),
- '1'
- ]
- })
+ expect(ast.hoists).toMatchObject([
+ {
+ type: NodeTypes.JS_CALL_EXPRESSION,
+ callee: CREATE_STATIC,
+ arguments: [
+ JSON.stringify(
+ `${repeat(
+ `1 + <` + `&`,
+ StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
+ )}
`
+ ),
+ '1'
+ ]
+ },
+ {
+ type: NodeTypes.JS_ARRAY_EXPRESSION
+ }
+ ])
})
test('should bail on runtime constant v-bind bindings', () => {
@@ -192,13 +213,16 @@ describe('stringify static html', () => {
]
}
)
- // the expression and the tree are still hoistable
- expect(ast.hoists.length).toBe(1)
- // ...but the hoisted tree should not be stringified
- expect(ast.hoists[0]).toMatchObject({
- // if it's stringified it will be NodeTypes.CALL_EXPRESSION
- type: NodeTypes.VNODE_CALL
- })
+ expect(ast.hoists).toMatchObject([
+ {
+ // the expression and the tree are still hoistable
+ // but if it's stringified it will be NodeTypes.CALL_EXPRESSION
+ type: NodeTypes.VNODE_CALL
+ },
+ {
+ type: NodeTypes.JS_ARRAY_EXPRESSION
+ }
+ ])
})
// #1128
@@ -209,10 +233,14 @@ describe('stringify static html', () => {
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
)}`
)
- expect(ast.hoists.length).toBe(1)
- expect(ast.hoists[0]).toMatchObject({
- type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION
- })
+ expect(ast.hoists).toMatchObject([
+ {
+ type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION
+ },
+ {
+ type: NodeTypes.JS_ARRAY_EXPRESSION
+ }
+ ])
const { ast: ast2 } = compileWithStringify(
``
)
- expect(ast2.hoists.length).toBe(1)
- expect(ast2.hoists[0]).toMatchObject({
- type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION
- })
+ expect(ast2.hoists).toMatchObject([
+ {
+ type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION
+ },
+ {
+ type: NodeTypes.JS_ARRAY_EXPRESSION
+ }
+ ])
})
test('should bail on non attribute bindings', () => {
@@ -233,10 +265,14 @@ describe('stringify static html', () => {
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
)}`
)
- expect(ast.hoists.length).toBe(1)
- expect(ast.hoists[0]).toMatchObject({
- type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION
- })
+ expect(ast.hoists).toMatchObject([
+ {
+ type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION
+ },
+ {
+ type: NodeTypes.JS_ARRAY_EXPRESSION
+ }
+ ])
const { ast: ast2 } = compileWithStringify(
``
)
- expect(ast2.hoists.length).toBe(1)
- expect(ast2.hoists[0]).toMatchObject({
- type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION
- })
+ expect(ast2.hoists).toMatchObject([
+ {
+ type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION
+ },
+ {
+ type: NodeTypes.JS_ARRAY_EXPRESSION
+ }
+ ])
})
test('should bail on tags that has placement constraints (eg.tables related tags)', () => {
@@ -257,10 +297,14 @@ describe('stringify static html', () => {
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
)}`
)
- expect(ast.hoists.length).toBe(1)
- expect(ast.hoists[0]).toMatchObject({
- type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION
- })
+ expect(ast.hoists).toMatchObject([
+ {
+ type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION
+ },
+ {
+ type: NodeTypes.JS_ARRAY_EXPRESSION
+ }
+ ])
})
test('should bail inside slots', () => {