import {
+ type CallExpression,
type CompilerOptions,
+ type ComponentNode,
type ElementNode,
ElementTypes,
ErrorCodes,
type IfBranchNode,
type IfNode,
NodeTypes,
+ RESOLVE_SKIP_COMPONENT,
type RootNode,
type SimpleExpressionNode,
type SkipNode,
+ type VNodeCall,
generate,
baseParse as parse,
transform,
options: CompilerOptions = { prefixIdentifiers: true },
): {
root: RootNode
- node: SkipNode
+ node: SkipNode | ComponentNode
} {
const ast = parse(template, options)
transform(ast, {
})
return {
root: ast,
- node: ast.children[0] as SkipNode,
+ node: ast.children[0] as SkipNode | ComponentNode,
}
}
describe('compiler: v-skip', () => {
describe('transform', () => {
test('basic', () => {
- const { root, node } = parseWithSkipTransform(`<div v-skip="ok"/>`)
+ const { root, node } = parseWithSkipTransform(`<div v-skip="ok"/>`) as {
+ root: RootNode
+ node: SkipNode
+ }
expect(node.type).toBe(NodeTypes.SKIP)
expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
expect(node.consequent.type === NodeTypes.JS_CALL_EXPRESSION).toBe(true)
test('with text children', () => {
const { root, node } = parseWithSkipTransform(
`<div v-skip="ok">foo</div>`,
- )
+ ) as { root: RootNode; node: SkipNode }
expect(node.type).toBe(NodeTypes.SKIP)
expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
expect((node.consequent as IfBranchNode).children.length).toBe(1)
test('with element children', () => {
const { root, node } = parseWithSkipTransform(
`<div v-skip="ok"><span/></div>`,
- )
+ ) as { root: RootNode; node: SkipNode }
expect(node.type).toBe(NodeTypes.SKIP)
expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
expect((node.consequent as IfBranchNode).children.length).toBe(1)
test('with component children', () => {
const { root, node } = parseWithSkipTransform(
`<div v-skip="ok"><Comp/></div>`,
- )
+ ) as { root: RootNode; node: SkipNode }
expect(node.type).toBe(NodeTypes.SKIP)
expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
expect((node.consequent as IfBranchNode).children.length).toBe(1)
test('with multiple children', () => {
const { root, node } = parseWithSkipTransform(
`<div v-skip="ok"><span/><Comp/></div>`,
- )
+ ) as { root: RootNode; node: SkipNode }
expect(node.type).toBe(NodeTypes.SKIP)
expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
expect((node.consequent as IfBranchNode).children.length).toBe(2)
test('nested v-skip', () => {
const { root, node } = parseWithSkipTransform(
`<div v-skip="ok"><span v-skip="nested"/></div>`,
- )
+ ) as { root: RootNode; node: SkipNode }
expect(node.type).toBe(NodeTypes.SKIP)
expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
expect((node.consequent as IfBranchNode).children.length).toBe(1)
test('v-skip with key', () => {
const { root, node } = parseWithSkipTransform(
`<div v-skip="nested" key="foo"/>`,
- )
+ ) as { root: RootNode; node: SkipNode }
expect(node.type).toBe(NodeTypes.SKIP)
expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.nested`)
expect(node.consequent.type === NodeTypes.JS_CALL_EXPRESSION).toBe(true)
test('v-else + v-skip', () => {
const { root, node } = parseWithSkipTransform(
`<div v-if="ok"/><div v-else v-skip="nested"/>`,
- )
+ ) as { root: RootNode; node: SkipNode }
expect(node.type).toBe(NodeTypes.IF)
const elseNode = node as unknown as IfNode
const branch = elseNode.branches[1]
test('v-else-if + v-skip', () => {
const { root, node } = parseWithSkipTransform(
`<div v-if="ok"/><div v-else-if="yes" v-skip="nested"/>`,
- )
+ ) as { root: RootNode; node: SkipNode }
expect(node.type).toBe(NodeTypes.IF)
const elseIfNode = node as unknown as IfNode
const branch = elseIfNode.branches[1]
})
test('on component', () => {
- const { root, node } = parseWithSkipTransform(`<Comp v-skip="ok"/>`)
- expect(node.type).toBe(NodeTypes.SKIP)
- expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
- expect(node.consequent.type === NodeTypes.JS_CALL_EXPRESSION).toBe(true)
- expect(node.alternate.children.length).toBe(1)
- expect((node.alternate.children[0] as ElementNode).tagType).toBe(
- ElementTypes.COMPONENT,
+ const { root, node } = parseWithSkipTransform(`<Comp v-skip="ok"/>`) as {
+ root: RootNode
+ node: ComponentNode
+ }
+ expect(node.type).toBe(NodeTypes.ELEMENT)
+ expect(node.tagType).toBe(ElementTypes.COMPONENT)
+ const codegenNode = node.codegenNode! as VNodeCall
+ expect(codegenNode.type).toBe(NodeTypes.VNODE_CALL)
+ const vnodeTag = codegenNode.tag as CallExpression
+ expect(vnodeTag.type).toBe(NodeTypes.JS_CALL_EXPRESSION)
+ expect(vnodeTag.callee).toBe(RESOLVE_SKIP_COMPONENT)
+ expect((vnodeTag.arguments[0] as SimpleExpressionNode).content).toBe(
+ `_ctx.ok`,
)
- expect((node.alternate.children[0] as ElementNode).tag).toBe(`Comp`)
expect(generate(root).code).toMatchSnapshot()
})
- test('on component with default slot', () => {
+ test.todo('on component with default slot', () => {
const { root, node } = parseWithSkipTransform(
`<Comp v-skip="ok">foo</Comp>`,
)
expect(generate(root).code).toMatchSnapshot()
})
- test('on component with multiple named slot', () => {
+ test.todo('on component with multiple named slot', () => {
const { root, node } = parseWithSkipTransform(
`<Comp v-skip="ok">
<template #default>default</template>
expect(generate(root).code).toMatchSnapshot()
})
- test('on component with multiple implicit slot', () => {
+ test.todo('on component with multiple implicit slot', () => {
const { root, node } = parseWithSkipTransform(
`<Comp v-skip="ok">
<span/>
`<component :is="Comp" v-skip="ok">
<slot/>
</component>`,
+ ) as { root: RootNode; node: ComponentNode }
+ expect(node.type).toBe(NodeTypes.ELEMENT)
+ expect(node.tagType).toBe(ElementTypes.COMPONENT)
+ const codegenNode = node.codegenNode! as VNodeCall
+ expect(codegenNode.type).toBe(NodeTypes.VNODE_CALL)
+ const vnodeTag = codegenNode.tag as CallExpression
+ expect(vnodeTag.type).toBe(NodeTypes.JS_CALL_EXPRESSION)
+ expect(vnodeTag.callee).toBe(RESOLVE_SKIP_COMPONENT)
+ expect((vnodeTag.arguments[0] as SimpleExpressionNode).content).toBe(
+ `_ctx.ok`,
)
- expect(node.type).toBe(NodeTypes.SKIP)
- expect((node.test as SimpleExpressionNode).content).toBe(`_ctx.ok`)
- expect((node.consequent as IfBranchNode).children.length).toBe(1)
- expect((node.consequent as IfBranchNode).children[0].type).toBe(
- NodeTypes.ELEMENT,
- )
- expect(
- ((node.consequent as IfBranchNode).children[0] as ElementNode).tag,
- ).toBe(`slot`)
- expect(node.alternate.children.length).toBe(1)
- expect((node.alternate.children[0] as ElementNode).tagType).toBe(
- ElementTypes.COMPONENT,
- )
- expect((node.alternate.children[0] as ElementNode).tag).toBe(`component`)
expect(generate(root).code).toMatchSnapshot()
})
+
+ test.todo('on Teleport', () => {})
+
+ test.todo('built-in components', () => {})
})
describe('errors', () => {
])
})
- test('on component without default slot', () => {
- const onError = vi.fn()
- parseWithSkipTransform(
- `<Comp v-skip="ok">
- <template #foo>foo</template>
- </Comp>`,
- { onError },
- )
- expect(onError.mock.calls[0]).toMatchObject([
- {
- code: ErrorCodes.X_V_SKIP_UNEXPECTED_SLOT,
- },
- ])
- })
-
- test('on component with default slot and slot props', () => {
- const onError = vi.fn()
- parseWithSkipTransform(
- `<Comp v-skip="ok">
- <template #default="foo">foo</template>
- </Comp>`,
- { onError },
- )
- expect(onError.mock.calls[0]).toMatchObject([
- {
- code: ErrorCodes.X_V_SKIP_UNEXPECTED_SLOT,
- },
- ])
- })
-
- test('on component with only dynamic slot', () => {
- const onError = vi.fn()
- parseWithSkipTransform(
- `<Comp v-skip="ok">
- <template #[foo]>foo</template>
- </Comp>`,
- { onError },
- )
- expect(onError.mock.calls[0]).toMatchObject([
- {
- code: ErrorCodes.X_V_SKIP_UNEXPECTED_SLOT,
- },
- ])
- })
-
test('with v-for', () => {
const onError = vi.fn()
parseWithSkipTransform(`<div v-for="i in items" v-skip="ok"/>`, {
`)
})
- test('on component with default slot', () => {
+ test.todo('on component with default slot', () => {
expect(compile(`<Comp v-skip="ok">foo</Comp>`).code).toMatchInlineSnapshot(`
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, createTextVNode: _createTextVNode } = require("vue")
const { ssrRenderComponent: _ssrRenderComponent, ssrRenderSkipComponent: _ssrRenderSkipComponent } = require("vue/server-renderer")
`)
})
- test('on component with multiple named slot', () => {
+ test.todo('on component with multiple named slot', () => {
expect(
compile(
`<Comp v-skip="ok">
`)
})
- test('on component with multiple implicit slot', () => {
+ test.todo('on component with multiple implicit slot', () => {
expect(
compile(
`<Comp v-skip="ok">
).code,
).toMatchInlineSnapshot(`
"const { resolveDynamicComponent: _resolveDynamicComponent, withCtx: _withCtx, renderSlot: _renderSlot, createVNode: _createVNode } = require("vue")
- const { ssrRenderSlot: _ssrRenderSlot, ssrRenderVNode: _ssrRenderVNode, ssrRenderSkipComponent: _ssrRenderSkipComponent } = require("vue/server-renderer")
+ const { ssrRenderSlot: _ssrRenderSlot, ssrRenderVNode: _ssrRenderVNode } = require("vue/server-renderer")
return function ssrRender(_ctx, _push, _parent, _attrs) {
- _ssrRenderSkipComponent(_push, _ctx.ok, _push, _createVNode(_resolveDynamicComponent(_ctx.Comp), _attrs, {
+ _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.Comp), _attrs, {
default: _withCtx((_, _push, _parent, _scopeId) => {
if (_push) {
_ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent, _scopeId)