-import { ElementNode, Namespace, TemplateChildNode } from './ast'
+import { ElementNode, Namespace, TemplateChildNode, ParentNode } from './ast'
import { TextModes } from './parse'
import { CompilerError } from './errors'
import {
export type HoistTransform = (
children: TemplateChildNode[],
- context: TransformContext
+ context: TransformContext,
+ parent: ParentNode
) => void
export interface TransformOptions {
ComponentNode,
TemplateNode,
ElementNode,
- VNodeCall
+ VNodeCall,
+ ParentNode
} from '../ast'
import { TransformContext } from '../transform'
import { PatchFlags, isString, isSymbol } from '@vue/shared'
export function hoistStatic(root: RootNode, context: TransformContext) {
walk(
- root.children,
+ root,
context,
new Map(),
// Root node is unfortunately non-hoistable due to potential parent
}
function walk(
- children: TemplateChildNode[],
+ node: ParentNode,
context: TransformContext,
resultCache: Map<TemplateChildNode, StaticType>,
doNotHoistNode: boolean = false
// stringficiation threshold is met.
let hasRuntimeConstant = false
+ const { children } = node
for (let i = 0; i < children.length; i++) {
const child = children[i]
// only plain elements & text calls are eligible for hoisting.
// walk further
if (child.type === NodeTypes.ELEMENT) {
- walk(child.children, context, resultCache)
+ walk(child, context, resultCache)
} else if (child.type === NodeTypes.FOR) {
// Do not hoist v-for single child because it has to be a block
- walk(child.children, context, resultCache, child.children.length === 1)
+ walk(child, context, resultCache, child.children.length === 1)
} else if (child.type === NodeTypes.IF) {
for (let i = 0; i < child.branches.length; i++) {
- const branchChildren = child.branches[i].children
// Do not hoist v-if single child because it has to be a block
- walk(branchChildren, context, resultCache, branchChildren.length === 1)
+ walk(
+ child.branches[i],
+ context,
+ resultCache,
+ child.branches[i].children.length === 1
+ )
}
}
}
if (!hasRuntimeConstant && hasHoistedNode && context.transformHoist) {
- context.transformHoist(children, context)
+ context.transformHoist(children, context, node)
}
}
})
})
- test('should bail on break content with innerHTML (eg.tables related tags)', () => {
+ test('should bail on tags that has placement constraints (eg.tables related tags)', () => {
const { ast } = compileWithStringify(
`<table><tbody>${repeat(
`<tr class="foo"><td>foo</td></tr>`,
type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION
})
})
+
+ test('should bail inside slots', () => {
+ const { ast } = compileWithStringify(
+ `<foo>${repeat(
+ `<div class="foo"></div>`,
+ StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
+ )}</foo>`
+ )
+ expect(ast.hoists.length).toBe(
+ StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
+ )
+ ast.hoists.forEach(node => {
+ expect(node).toMatchObject({
+ type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION
+ })
+ })
+
+ const { ast: ast2 } = compileWithStringify(
+ `<foo><template #foo>${repeat(
+ `<div class="foo"></div>`,
+ StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
+ )}</template></foo>`
+ )
+ expect(ast2.hoists.length).toBe(
+ StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
+ )
+ ast2.hoists.forEach(node => {
+ expect(node).toMatchObject({
+ type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION
+ })
+ })
+ })
})
*
* This optimization is only performed in Node.js.
*/
-export const stringifyStatic: HoistTransform = (children, context) => {
+export const stringifyStatic: HoistTransform = (children, context, parent) => {
+ if (
+ parent.type === NodeTypes.ELEMENT &&
+ (parent.tagType === ElementTypes.COMPONENT ||
+ parent.tagType === ElementTypes.TEMPLATE)
+ ) {
+ return
+ }
+
let nc = 0 // current node count
let ec = 0 // current element with binding count
const currentChunk: StringifiableNode[] = []