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( `
hello
hello
` ) - 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( `
${repeat( @@ -220,10 +248,14 @@ describe('stringify static html', () => { StringifyThresholds.ELEMENT_WITH_BINDING_COUNT )}
` ) - 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( `
${repeat( @@ -244,10 +280,14 @@ describe('stringify static html', () => { StringifyThresholds.ELEMENT_WITH_BINDING_COUNT )}
` ) - 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', () => {