From f05f2171b11679ccb892e6f9a0f037cef115e8c2 Mon Sep 17 00:00:00 2001 From: daiwei Date: Wed, 26 Nov 2025 09:25:20 +0800 Subject: [PATCH 1/3] test(vIf): add `v-else`/`v-else-if` error tests and refactor comment node check to use `isCommentOrWhitespace` utility. --- .../__tests__/transforms/vIf.spec.ts | 589 +++++++++++++++++- .../src/transforms/transformComment.ts | 10 +- 2 files changed, 587 insertions(+), 12 deletions(-) diff --git a/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts b/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts index 728b3f9c41f..ab386430a2e 100644 --- a/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts @@ -11,7 +11,7 @@ import { transformVOnce, transformVText, } from '../../src' -import { NodeTypes } from '@vue/compiler-dom' +import { ErrorCodes, NodeTypes, type RootNode } from '@vue/compiler-dom' const compileWithVIf = makeCompile({ nodeTransforms: [ @@ -380,7 +380,588 @@ describe('compiler: v-if', () => { ]) }) - describe.todo('errors') - describe.todo('codegen') - test.todo('v-on with v-if') + describe('errors', () => { + test('error on v-else missing adjacent v-if', () => { + const onError = vi.fn() + + { + const { ir } = compileWithVIf(`
`, { onError }) + expect(onError.mock.calls[0]).toMatchObject([ + { + code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, + loc: ir.node.loc, + }, + ]) + } + + { + const { ir } = compileWithVIf(`
`, { + onError, + }) + expect(onError.mock.calls[1]).toMatchObject([ + { + code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, + loc: (ir.block.node as RootNode).children[1].loc, + }, + ]) + } + + { + const { ir } = compileWithVIf(`
foo
`, { onError }) + expect(onError.mock.calls[2]).toMatchObject([ + { + code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, + loc: (ir.block.node as RootNode).children[2].loc, + }, + ]) + } + + { + const { ir } = compileWithVIf(`
foo
`, { + onError, + }) + expect(onError.mock.calls[3]).toMatchObject([ + { + code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, + loc: (ir.block.node as RootNode).children[2].loc, + }, + ]) + } + + // Non-breaking space + { + const { ir } = compileWithVIf(`
\u00a0
`, { + onError, + }) + expect(onError.mock.calls[4]).toMatchObject([ + { + code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, + loc: (ir.block.node as RootNode).children[2].loc, + }, + ]) + } + }) + + test('error on v-else-if missing adjacent v-if or v-else-if', () => { + const onError = vi.fn() + { + const { ir } = compileWithVIf(`
`, { + onError, + }) + expect(onError.mock.calls[0]).toMatchObject([ + { + code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, + loc: ir.node.loc, + }, + ]) + } + { + const { ir } = compileWithVIf(`
`, { + onError, + }) + expect(onError.mock.calls[1]).toMatchObject([ + { + code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, + loc: (ir.block.node as RootNode).children[1].loc, + }, + ]) + } + { + const { ir } = compileWithVIf(`
foo
`, { + onError, + }) + expect(onError.mock.calls[2]).toMatchObject([ + { + code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, + loc: (ir.block.node as RootNode).children[2].loc, + }, + ]) + } + { + const { ir } = compileWithVIf( + `
foo
`, + { onError }, + ) + expect(onError.mock.calls[3]).toMatchObject([ + { + code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, + loc: (ir.block.node as RootNode).children[2].loc, + }, + ]) + } + { + // Non-breaking space + const { ir } = compileWithVIf( + `
\u00a0
`, + { onError }, + ) + expect(onError.mock.calls[4]).toMatchObject([ + { + code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, + loc: (ir.block.node as RootNode).children[2].loc, + }, + ]) + } + + { + const { ir } = compileWithVIf( + `
`, + { onError }, + ) + expect(onError.mock.calls[5]).toMatchObject([ + { + code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, + loc: (ir.block.node as RootNode).children[2].loc, + }, + ]) + } + }) + + test('error on adjacent v-else', () => { + const onError = vi.fn() + + const { ir } = compileWithVIf( + `
`, + { onError }, + ) + + expect(onError.mock.calls[0]).toMatchObject([ + { + code: ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, + loc: (ir.block.node as RootNode).children[2].loc, + }, + ]) + }) + }) + + // describe('codegen', () => { + // function assertSharedCodegen( + // node: IfConditionalExpression, + // depth: number = 0, + // hasElse: boolean = false, + // ) { + // expect(node).toMatchObject({ + // 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, + // }, + // }, + // }) + // } + + // test('basic v-if', () => { + // const { + // root, + // node: { codegenNode }, + // } = compileWithVIf(`
`) + // assertSharedCodegen(codegenNode) + // expect(codegenNode.consequent).toMatchObject({ + // tag: `"div"`, + // props: createObjectMatcher({ key: `[0]` }), + // }) + // expect(codegenNode.alternate).toMatchObject({ + // type: NodeTypes.JS_CALL_EXPRESSION, + // callee: CREATE_COMMENT, + // }) + // expect(generate(root).code).toMatchSnapshot() + // }) + + // test('template v-if', () => { + // const { + // root, + // node: { codegenNode }, + // } = compileWithVIf(``) + // assertSharedCodegen(codegenNode) + // 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' }, + // ], + // }) + // expect(codegenNode.alternate).toMatchObject({ + // type: NodeTypes.JS_CALL_EXPRESSION, + // callee: CREATE_COMMENT, + // }) + // expect(generate(root).code).toMatchSnapshot() + // }) + + // test('template v-if w/ single child', () => { + // const { + // root, + // node: { codegenNode }, + // } = compileWithVIf(``) + // expect(codegenNode.consequent).toMatchObject({ + // type: NodeTypes.JS_CALL_EXPRESSION, + // callee: RENDER_SLOT, + // arguments: ['$slots', '"default"', createObjectMatcher({ key: `[0]` })], + // }) + // expect(generate(root).code).toMatchSnapshot() + // }) + + // test('v-if on ', () => { + // const { + // root, + // node: { codegenNode }, + // } = compileWithVIf(``) + // expect(codegenNode.consequent).toMatchObject({ + // type: NodeTypes.JS_CALL_EXPRESSION, + // callee: RENDER_SLOT, + // arguments: ['$slots', '"default"', createObjectMatcher({ key: `[0]` })], + // }) + // expect(generate(root).code).toMatchSnapshot() + // }) + + // test('v-if + v-else', () => { + // const { + // root, + // node: { codegenNode }, + // } = compileWithVIf(`

`) + // assertSharedCodegen(codegenNode, 0, true) + // 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() + // }) + + // test('v-if + v-else-if', () => { + // const { + // root, + // node: { codegenNode }, + // } = compileWithVIf(`

`) + // assertSharedCodegen(codegenNode, 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() + // }) + + // test('v-if + v-else-if + v-else', () => { + // const { + // root, + // node: { codegenNode }, + // } = compileWithVIf( + // `

`, + // ) + // assertSharedCodegen(codegenNode, 1, true) + // 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() + // }) + + // test('multiple v-if that are sibling nodes should have different keys', () => { + // const { root } = compileWithVIf( + // `

`, + // {}, + // 0 /* returnIndex, just give the default value */, + // 2 /* childrenLen */, + // ) + + // const ifNode = root.children[0] as IfNode & { + // codegenNode: IfConditionalExpression + // } + // expect(ifNode.codegenNode.consequent).toMatchObject({ + // tag: `"div"`, + // props: createObjectMatcher({ key: `[0]` }), + // }) + // const ifNode2 = root.children[1] as IfNode & { + // codegenNode: IfConditionalExpression + // } + // expect(ifNode2.codegenNode.consequent).toMatchObject({ + // tag: `"p"`, + // props: createObjectMatcher({ key: `[1]` }), + // }) + // expect(generate(root).code).toMatchSnapshot() + // }) + + // test('increasing key: v-if + v-else-if + v-else', () => { + // const { root } = compileWithVIf( + // `

`, + // {}, + // 0 /* returnIndex, just give the default value */, + // 2 /* childrenLen */, + // ) + // const ifNode = root.children[0] as IfNode & { + // codegenNode: IfConditionalExpression + // } + // expect(ifNode.codegenNode.consequent).toMatchObject({ + // tag: `"div"`, + // props: createObjectMatcher({ key: `[0]` }), + // }) + // expect(ifNode.codegenNode.alternate).toMatchObject({ + // tag: `"p"`, + // props: createObjectMatcher({ key: `[1]` }), + // }) + // const ifNode2 = root.children[1] as IfNode & { + // codegenNode: IfConditionalExpression + // } + // expect(ifNode2.codegenNode.consequent).toMatchObject({ + // tag: `"div"`, + // props: createObjectMatcher({ key: `[2]` }), + // }) + // const branch = ifNode2.codegenNode.alternate as IfConditionalExpression + // expect(branch.consequent).toMatchObject({ + // tag: `"p"`, + // props: createObjectMatcher({ key: `[3]` }), + // }) + // expect(branch.alternate).toMatchObject({ + // tag: `"p"`, + // props: createObjectMatcher({ key: `[4]` }), + // }) + // expect(generate(root).code).toMatchSnapshot() + // }) + + // test('key injection (only v-bind)', () => { + // const { + // node: { codegenNode }, + // } = compileWithVIf(`

`) + // const branch1 = codegenNode.consequent as VNodeCall + // expect(branch1.props).toMatchObject({ + // type: NodeTypes.JS_CALL_EXPRESSION, + // callee: NORMALIZE_PROPS, + // arguments: [ + // { + // type: NodeTypes.JS_CALL_EXPRESSION, + // callee: MERGE_PROPS, + // arguments: [ + // createObjectMatcher({ key: `[0]` }), + // { content: `obj` }, + // ], + // }, + // ], + // }) + // }) + + // test('key injection (before v-bind)', () => { + // const { + // node: { codegenNode }, + // } = compileWithVIf(`
`) + // const branch1 = codegenNode.consequent as VNodeCall + // expect(branch1.props).toMatchObject({ + // type: NodeTypes.JS_CALL_EXPRESSION, + // callee: MERGE_PROPS, + // arguments: [ + // createObjectMatcher({ + // key: '[0]', + // id: 'foo', + // }), + // { content: `obj` }, + // ], + // }) + // }) + + // test('key injection (after v-bind)', () => { + // const { + // node: { codegenNode }, + // } = compileWithVIf(`
`) + // const branch1 = codegenNode.consequent as VNodeCall + // expect(branch1.props).toMatchObject({ + // type: NodeTypes.JS_CALL_EXPRESSION, + // callee: MERGE_PROPS, + // arguments: [ + // createObjectMatcher({ key: `[0]` }), + // { content: `obj` }, + // createObjectMatcher({ + // id: 'foo', + // }), + // ], + // }) + // }) + + // test('key injection (w/ custom directive)', () => { + // const { + // node: { codegenNode }, + // } = compileWithVIf(`
`) + // const branch1 = codegenNode.consequent as VNodeCall + // expect(branch1.directives).not.toBeUndefined() + // expect(branch1.props).toMatchObject(createObjectMatcher({ key: `[0]` })) + // }) + + // // #6631 + // test('avoid duplicate keys', () => { + // const { + // node: { codegenNode }, + // } = compileWithVIf(`
`) + // const branch1 = codegenNode.consequent as VNodeCall + // expect(branch1.props).toMatchObject({ + // type: NodeTypes.JS_CALL_EXPRESSION, + // callee: MERGE_PROPS, + // arguments: [ + // createObjectMatcher({ + // key: 'custom_key', + // }), + // { content: `obj` }, + // ], + // }) + // }) + + // test('with spaces between branches', () => { + // const { + // node: { codegenNode }, + // } = compileWithVIf(`
`) + // expect(codegenNode.consequent).toMatchObject({ + // tag: `"div"`, + // props: createObjectMatcher({ key: `[0]` }), + // }) + // const branch = codegenNode.alternate as ConditionalExpression + // expect(branch.consequent).toMatchObject({ + // tag: `"div"`, + // props: createObjectMatcher({ key: `[1]` }), + // }) + // expect(branch.alternate).toMatchObject({ + // tag: `"div"`, + // props: createObjectMatcher({ key: `[2]` }), + // }) + // }) + + // test('with comments', () => { + // const { node } = compileWithVIf(` + // + // `) + // expect(node.type).toBe(NodeTypes.IF) + // expect(node.branches.length).toBe(1) + + // const b1 = node.branches[0] + // expect((b1.condition as SimpleExpressionNode).content).toBe(`ok`) + // expect(b1.children.length).toBe(4) + + // expect(b1.children[0].type).toBe(NodeTypes.COMMENT) + // expect((b1.children[0] as CommentNode).content).toBe(`comment1`) + + // expect(b1.children[1].type).toBe(NodeTypes.IF) + // expect((b1.children[1] as IfNode).branches.length).toBe(2) + // const b1b1: ElementNode = (b1.children[1] as IfNode).branches[0] + // .children[0] as ElementNode + // expect(b1b1.type).toBe(NodeTypes.ELEMENT) + // expect(b1b1.tag).toBe('div') + // expect(b1b1.children[0].type).toBe(NodeTypes.COMMENT) + // expect((b1b1.children[0] as CommentNode).content).toBe('comment2') + + // const b1b2: IfBranchNode = (b1.children[1] as IfNode) + // .branches[1] as IfBranchNode + // expect(b1b2.children[0].type).toBe(NodeTypes.COMMENT) + // expect((b1b2.children[0] as CommentNode).content).toBe(`comment3`) + // expect(b1b2.children[1].type).toBe(NodeTypes.ELEMENT) + // expect((b1b2.children[1] as ElementNode).tag).toBe(`b`) + + // expect(b1.children[2].type).toBe(NodeTypes.COMMENT) + // expect((b1.children[2] as CommentNode).content).toBe(`comment4`) + + // expect(b1.children[3].type).toBe(NodeTypes.ELEMENT) + // expect((b1.children[3] as ElementNode).tag).toBe(`p`) + // }) + + // // #6843 + // test('should parse correctly with comments: true in prod', () => { + // __DEV__ = false + // compileWithVIf( + // ` + // + // `, + // { comments: true }, + // ) + // __DEV__ = true + // }) + // }) + + // test('v-on with v-if', () => { + // const { + // node: { codegenNode }, + // } = compileWithVIf( + // ``, + // ) + + // expect((codegenNode.consequent as any).props.type).toBe( + // NodeTypes.JS_CALL_EXPRESSION, + // ) + // expect((codegenNode.consequent as any).props.callee).toBe(MERGE_PROPS) + // expect( + // (codegenNode.consequent as any).props.arguments[0].properties[0].value + // .content, + // ).toBe('0') + // expect((codegenNode.consequent as any).props.arguments[1].callee).toBe( + // TO_HANDLERS, + // ) + // }) }) diff --git a/packages/compiler-vapor/src/transforms/transformComment.ts b/packages/compiler-vapor/src/transforms/transformComment.ts index f85498febce..3850320bf2a 100644 --- a/packages/compiler-vapor/src/transforms/transformComment.ts +++ b/packages/compiler-vapor/src/transforms/transformComment.ts @@ -3,6 +3,7 @@ import { type ElementNode, NodeTypes, type TemplateChildNode, + isCommentOrWhitespace, } from '@vue/compiler-dom' import type { NodeTransform, TransformContext } from '../transform' import { DynamicFlag } from '../ir' @@ -31,7 +32,7 @@ export function getSiblingIf( let i = siblings.indexOf(context.node) while (reverse ? --i >= 0 : ++i < siblings.length) { sibling = siblings[i] - if (!isCommentLike(sibling)) { + if (!isCommentOrWhitespace(sibling)) { break } } @@ -48,10 +49,3 @@ export function getSiblingIf( return sibling } } - -function isCommentLike(node: TemplateChildNode) { - return ( - node.type === NodeTypes.COMMENT || - (node.type === NodeTypes.TEXT && !node.content.trim().length) - ) -} From 174656a10cdc40c0679a8f6ba2077e41d0170074 Mon Sep 17 00:00:00 2001 From: daiwei Date: Wed, 26 Nov 2025 09:51:47 +0800 Subject: [PATCH 2/3] test(vIf): remove commented-out code --- .../__tests__/transforms/vIf.spec.ts | 431 ------------------ 1 file changed, 431 deletions(-) diff --git a/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts b/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts index ab386430a2e..31ae5220b2b 100644 --- a/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts @@ -533,435 +533,4 @@ describe('compiler: v-if', () => { ]) }) }) - - // describe('codegen', () => { - // function assertSharedCodegen( - // node: IfConditionalExpression, - // depth: number = 0, - // hasElse: boolean = false, - // ) { - // expect(node).toMatchObject({ - // 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, - // }, - // }, - // }) - // } - - // test('basic v-if', () => { - // const { - // root, - // node: { codegenNode }, - // } = compileWithVIf(`
`) - // assertSharedCodegen(codegenNode) - // expect(codegenNode.consequent).toMatchObject({ - // tag: `"div"`, - // props: createObjectMatcher({ key: `[0]` }), - // }) - // expect(codegenNode.alternate).toMatchObject({ - // type: NodeTypes.JS_CALL_EXPRESSION, - // callee: CREATE_COMMENT, - // }) - // expect(generate(root).code).toMatchSnapshot() - // }) - - // test('template v-if', () => { - // const { - // root, - // node: { codegenNode }, - // } = compileWithVIf(``) - // assertSharedCodegen(codegenNode) - // 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' }, - // ], - // }) - // expect(codegenNode.alternate).toMatchObject({ - // type: NodeTypes.JS_CALL_EXPRESSION, - // callee: CREATE_COMMENT, - // }) - // expect(generate(root).code).toMatchSnapshot() - // }) - - // test('template v-if w/ single child', () => { - // const { - // root, - // node: { codegenNode }, - // } = compileWithVIf(``) - // expect(codegenNode.consequent).toMatchObject({ - // type: NodeTypes.JS_CALL_EXPRESSION, - // callee: RENDER_SLOT, - // arguments: ['$slots', '"default"', createObjectMatcher({ key: `[0]` })], - // }) - // expect(generate(root).code).toMatchSnapshot() - // }) - - // test('v-if on ', () => { - // const { - // root, - // node: { codegenNode }, - // } = compileWithVIf(``) - // expect(codegenNode.consequent).toMatchObject({ - // type: NodeTypes.JS_CALL_EXPRESSION, - // callee: RENDER_SLOT, - // arguments: ['$slots', '"default"', createObjectMatcher({ key: `[0]` })], - // }) - // expect(generate(root).code).toMatchSnapshot() - // }) - - // test('v-if + v-else', () => { - // const { - // root, - // node: { codegenNode }, - // } = compileWithVIf(`

`) - // assertSharedCodegen(codegenNode, 0, true) - // 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() - // }) - - // test('v-if + v-else-if', () => { - // const { - // root, - // node: { codegenNode }, - // } = compileWithVIf(`

`) - // assertSharedCodegen(codegenNode, 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() - // }) - - // test('v-if + v-else-if + v-else', () => { - // const { - // root, - // node: { codegenNode }, - // } = compileWithVIf( - // `

`, - // ) - // assertSharedCodegen(codegenNode, 1, true) - // 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() - // }) - - // test('multiple v-if that are sibling nodes should have different keys', () => { - // const { root } = compileWithVIf( - // `

`, - // {}, - // 0 /* returnIndex, just give the default value */, - // 2 /* childrenLen */, - // ) - - // const ifNode = root.children[0] as IfNode & { - // codegenNode: IfConditionalExpression - // } - // expect(ifNode.codegenNode.consequent).toMatchObject({ - // tag: `"div"`, - // props: createObjectMatcher({ key: `[0]` }), - // }) - // const ifNode2 = root.children[1] as IfNode & { - // codegenNode: IfConditionalExpression - // } - // expect(ifNode2.codegenNode.consequent).toMatchObject({ - // tag: `"p"`, - // props: createObjectMatcher({ key: `[1]` }), - // }) - // expect(generate(root).code).toMatchSnapshot() - // }) - - // test('increasing key: v-if + v-else-if + v-else', () => { - // const { root } = compileWithVIf( - // `

`, - // {}, - // 0 /* returnIndex, just give the default value */, - // 2 /* childrenLen */, - // ) - // const ifNode = root.children[0] as IfNode & { - // codegenNode: IfConditionalExpression - // } - // expect(ifNode.codegenNode.consequent).toMatchObject({ - // tag: `"div"`, - // props: createObjectMatcher({ key: `[0]` }), - // }) - // expect(ifNode.codegenNode.alternate).toMatchObject({ - // tag: `"p"`, - // props: createObjectMatcher({ key: `[1]` }), - // }) - // const ifNode2 = root.children[1] as IfNode & { - // codegenNode: IfConditionalExpression - // } - // expect(ifNode2.codegenNode.consequent).toMatchObject({ - // tag: `"div"`, - // props: createObjectMatcher({ key: `[2]` }), - // }) - // const branch = ifNode2.codegenNode.alternate as IfConditionalExpression - // expect(branch.consequent).toMatchObject({ - // tag: `"p"`, - // props: createObjectMatcher({ key: `[3]` }), - // }) - // expect(branch.alternate).toMatchObject({ - // tag: `"p"`, - // props: createObjectMatcher({ key: `[4]` }), - // }) - // expect(generate(root).code).toMatchSnapshot() - // }) - - // test('key injection (only v-bind)', () => { - // const { - // node: { codegenNode }, - // } = compileWithVIf(`

`) - // const branch1 = codegenNode.consequent as VNodeCall - // expect(branch1.props).toMatchObject({ - // type: NodeTypes.JS_CALL_EXPRESSION, - // callee: NORMALIZE_PROPS, - // arguments: [ - // { - // type: NodeTypes.JS_CALL_EXPRESSION, - // callee: MERGE_PROPS, - // arguments: [ - // createObjectMatcher({ key: `[0]` }), - // { content: `obj` }, - // ], - // }, - // ], - // }) - // }) - - // test('key injection (before v-bind)', () => { - // const { - // node: { codegenNode }, - // } = compileWithVIf(`
`) - // const branch1 = codegenNode.consequent as VNodeCall - // expect(branch1.props).toMatchObject({ - // type: NodeTypes.JS_CALL_EXPRESSION, - // callee: MERGE_PROPS, - // arguments: [ - // createObjectMatcher({ - // key: '[0]', - // id: 'foo', - // }), - // { content: `obj` }, - // ], - // }) - // }) - - // test('key injection (after v-bind)', () => { - // const { - // node: { codegenNode }, - // } = compileWithVIf(`
`) - // const branch1 = codegenNode.consequent as VNodeCall - // expect(branch1.props).toMatchObject({ - // type: NodeTypes.JS_CALL_EXPRESSION, - // callee: MERGE_PROPS, - // arguments: [ - // createObjectMatcher({ key: `[0]` }), - // { content: `obj` }, - // createObjectMatcher({ - // id: 'foo', - // }), - // ], - // }) - // }) - - // test('key injection (w/ custom directive)', () => { - // const { - // node: { codegenNode }, - // } = compileWithVIf(`
`) - // const branch1 = codegenNode.consequent as VNodeCall - // expect(branch1.directives).not.toBeUndefined() - // expect(branch1.props).toMatchObject(createObjectMatcher({ key: `[0]` })) - // }) - - // // #6631 - // test('avoid duplicate keys', () => { - // const { - // node: { codegenNode }, - // } = compileWithVIf(`
`) - // const branch1 = codegenNode.consequent as VNodeCall - // expect(branch1.props).toMatchObject({ - // type: NodeTypes.JS_CALL_EXPRESSION, - // callee: MERGE_PROPS, - // arguments: [ - // createObjectMatcher({ - // key: 'custom_key', - // }), - // { content: `obj` }, - // ], - // }) - // }) - - // test('with spaces between branches', () => { - // const { - // node: { codegenNode }, - // } = compileWithVIf(`
`) - // expect(codegenNode.consequent).toMatchObject({ - // tag: `"div"`, - // props: createObjectMatcher({ key: `[0]` }), - // }) - // const branch = codegenNode.alternate as ConditionalExpression - // expect(branch.consequent).toMatchObject({ - // tag: `"div"`, - // props: createObjectMatcher({ key: `[1]` }), - // }) - // expect(branch.alternate).toMatchObject({ - // tag: `"div"`, - // props: createObjectMatcher({ key: `[2]` }), - // }) - // }) - - // test('with comments', () => { - // const { node } = compileWithVIf(` - // - // `) - // expect(node.type).toBe(NodeTypes.IF) - // expect(node.branches.length).toBe(1) - - // const b1 = node.branches[0] - // expect((b1.condition as SimpleExpressionNode).content).toBe(`ok`) - // expect(b1.children.length).toBe(4) - - // expect(b1.children[0].type).toBe(NodeTypes.COMMENT) - // expect((b1.children[0] as CommentNode).content).toBe(`comment1`) - - // expect(b1.children[1].type).toBe(NodeTypes.IF) - // expect((b1.children[1] as IfNode).branches.length).toBe(2) - // const b1b1: ElementNode = (b1.children[1] as IfNode).branches[0] - // .children[0] as ElementNode - // expect(b1b1.type).toBe(NodeTypes.ELEMENT) - // expect(b1b1.tag).toBe('div') - // expect(b1b1.children[0].type).toBe(NodeTypes.COMMENT) - // expect((b1b1.children[0] as CommentNode).content).toBe('comment2') - - // const b1b2: IfBranchNode = (b1.children[1] as IfNode) - // .branches[1] as IfBranchNode - // expect(b1b2.children[0].type).toBe(NodeTypes.COMMENT) - // expect((b1b2.children[0] as CommentNode).content).toBe(`comment3`) - // expect(b1b2.children[1].type).toBe(NodeTypes.ELEMENT) - // expect((b1b2.children[1] as ElementNode).tag).toBe(`b`) - - // expect(b1.children[2].type).toBe(NodeTypes.COMMENT) - // expect((b1.children[2] as CommentNode).content).toBe(`comment4`) - - // expect(b1.children[3].type).toBe(NodeTypes.ELEMENT) - // expect((b1.children[3] as ElementNode).tag).toBe(`p`) - // }) - - // // #6843 - // test('should parse correctly with comments: true in prod', () => { - // __DEV__ = false - // compileWithVIf( - // ` - // - // `, - // { comments: true }, - // ) - // __DEV__ = true - // }) - // }) - - // test('v-on with v-if', () => { - // const { - // node: { codegenNode }, - // } = compileWithVIf( - // ``, - // ) - - // expect((codegenNode.consequent as any).props.type).toBe( - // NodeTypes.JS_CALL_EXPRESSION, - // ) - // expect((codegenNode.consequent as any).props.callee).toBe(MERGE_PROPS) - // expect( - // (codegenNode.consequent as any).props.arguments[0].properties[0].value - // .content, - // ).toBe('0') - // expect((codegenNode.consequent as any).props.arguments[1].callee).toBe( - // TO_HANDLERS, - // ) - // }) }) From 43d86801f0d639f141ddb402f5ce53df97ee7071 Mon Sep 17 00:00:00 2001 From: daiwei Date: Wed, 26 Nov 2025 09:56:48 +0800 Subject: [PATCH 3/3] test(vIf): add test for v-on with v-if handling --- .../transforms/__snapshots__/vIf.spec.ts.snap | 14 +++++++++++ .../__tests__/transforms/vIf.spec.ts | 24 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap index 608e4bac510..232aba02a64 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap @@ -308,3 +308,17 @@ export function render(_ctx) { return n0 }" `; + +exports[`compiler: v-if > v-on with v-if 1`] = ` +"import { setDynamicEvents as _setDynamicEvents, renderEffect as _renderEffect, createIf as _createIf, template as _template } from 'vue'; +const t0 = _template("", true) + +export function render(_ctx) { + const n0 = _createIf(() => (true), () => { + const n2 = t0() + _renderEffect(() => _setDynamicEvents(n2, { click: _ctx.clickEvent })) + return n2 + }, null, true) + return n0 +}" +`; diff --git a/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts b/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts index 31ae5220b2b..482794356ed 100644 --- a/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts @@ -380,6 +380,30 @@ describe('compiler: v-if', () => { ]) }) + test('v-on with v-if', () => { + const { code, ir } = compileWithVIf( + ``, + ) + expect(code).toMatchSnapshot() + expect([...ir.template.keys()]).toEqual(['']) + + expect(ir.block.returns).toEqual([0]) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.IF, + condition: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'true', + isStatic: false, + }, + positive: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 0 }], + }, + }, + }) + }) + describe('errors', () => { test('error on v-else missing adjacent v-if', () => { const onError = vi.fn()