wip: add TransformTransition

This commit is contained in:
daiwei 2025-03-07 22:17:19 +08:00
parent 05d9f53c08
commit 1511d6cd60
6 changed files with 110 additions and 35 deletions

View File

@ -76,4 +76,5 @@ export {
} from './errors' } from './errors'
export { resolveModifiers } from './transforms/vOn' export { resolveModifiers } from './transforms/vOn'
export { isValidHTMLNesting } from './htmlNesting' export { isValidHTMLNesting } from './htmlNesting'
export { postTransformTransition } from './transforms/Transition'
export * from '@vue/compiler-core' export * from '@vue/compiler-core'

View File

@ -1,4 +1,5 @@
import { import {
type CompilerError,
type ComponentNode, type ComponentNode,
ElementTypes, ElementTypes,
type IfBranchNode, type IfBranchNode,
@ -15,40 +16,43 @@ export const transformTransition: NodeTransform = (node, context) => {
) { ) {
const component = context.isBuiltInComponent(node.tag) const component = context.isBuiltInComponent(node.tag)
if (component === TRANSITION) { if (component === TRANSITION) {
return () => { return postTransformTransition(node, context.onError)
if (!node.children.length) { }
return }
} }
// warn multiple transition children export function postTransformTransition(
if (hasMultipleChildren(node)) { node: ComponentNode,
context.onError( onError: (error: CompilerError) => void,
createDOMCompilerError( ): () => void {
DOMErrorCodes.X_TRANSITION_INVALID_CHILDREN, return () => {
{ if (!node.children.length) {
start: node.children[0].loc.start, return
end: node.children[node.children.length - 1].loc.end, }
source: '',
},
),
)
}
// check if it's s single child w/ v-show if (hasMultipleChildren(node)) {
// if yes, inject "persisted: true" to the transition props onError(
const child = node.children[0] createDOMCompilerError(DOMErrorCodes.X_TRANSITION_INVALID_CHILDREN, {
if (child.type === NodeTypes.ELEMENT) { start: node.children[0].loc.start,
for (const p of child.props) { end: node.children[node.children.length - 1].loc.end,
if (p.type === NodeTypes.DIRECTIVE && p.name === 'show') { source: '',
node.props.push({ }),
type: NodeTypes.ATTRIBUTE, )
name: 'persisted', }
nameLoc: node.loc,
value: undefined, // check if it's s single child w/ v-show
loc: node.loc, // if yes, inject "persisted: true" to the transition props
}) const child = node.children[0]
} if (child.type === NodeTypes.ELEMENT) {
} for (const p of child.props) {
if (p.type === NodeTypes.DIRECTIVE && p.name === 'show') {
node.props.push({
type: NodeTypes.ATTRIBUTE,
name: 'persisted',
nameLoc: node.loc,
value: undefined,
loc: node.loc,
})
} }
} }
} }

View File

@ -5,9 +5,11 @@ import {
transformText, transformText,
transformVBind, transformVBind,
transformVIf, transformVIf,
transformVShow,
transformVSlot, transformVSlot,
} from '@vue/compiler-vapor' } from '@vue/compiler-vapor'
import { expect } from 'vitest' import { transformTransition } from '../../src/transforms/transformTransition'
import { DOMErrorCodes } from '@vue/compiler-dom'
const compileWithElementTransform = makeCompile({ const compileWithElementTransform = makeCompile({
nodeTransforms: [ nodeTransforms: [
@ -16,9 +18,11 @@ const compileWithElementTransform = makeCompile({
transformElement, transformElement,
transformVSlot, transformVSlot,
transformChildren, transformChildren,
transformTransition,
], ],
directiveTransforms: { directiveTransforms: {
bind: transformVBind, bind: transformVBind,
show: transformVShow,
}, },
}) })
@ -52,4 +56,33 @@ describe('compiler: transition', () => {
// should preserve key // should preserve key
expect(code).contains('n0.$key = _ctx.key') expect(code).contains('n0.$key = _ctx.key')
}) })
test('warns if multiple children', () => {
const onError = vi.fn()
compileWithElementTransform(
`<Transition>
<h1>foo</h1>
<h2>bar</h2>
</Transition>`,
{
onError,
},
)
expect(onError).toHaveBeenCalled()
expect(onError.mock.calls).toMatchObject([
[{ code: DOMErrorCodes.X_TRANSITION_INVALID_CHILDREN }],
])
})
test('inject persisted when child has v-show', () => {
expect(
compileWithElementTransform(`
<Transition>
<div v-show="ok" />
</Transition>
`).code,
).toMatchSnapshot()
})
// TODO more tests
}) })

View File

@ -1,13 +1,33 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`compiler: transition > basic 1`] = ` exports[`compiler: transition > basic 1`] = `
"import { VaporTransition as _VaporTransition, createComponent as _createComponent, template as _template } from 'vue'; "import { VaporTransition as _VaporTransition, applyVShow as _applyVShow, createComponent as _createComponent, template as _template } from 'vue';
const t0 = _template("<h1>foo</h1>") const t0 = _template("<h1>foo</h1>")
export function render(_ctx) { export function render(_ctx) {
const n1 = _createComponent(_VaporTransition, { appear: () => ("") }, { const n1 = _createComponent(_VaporTransition, {
appear: () => (""),
persisted: () => ("")
}, {
"default": () => { "default": () => {
const n0 = t0() const n0 = t0()
_applyVShow(n0, () => (_ctx.show))
return n0
}
}, true)
return n1
}"
`;
exports[`compiler: transition > inject persisted when child has v-show 1`] = `
"import { VaporTransition as _VaporTransition, applyVShow as _applyVShow, createComponent as _createComponent, template as _template } from 'vue';
const t0 = _template("<div></div>")
export function render(_ctx) {
const n1 = _createComponent(_VaporTransition, { persisted: () => ("") }, {
"default": () => {
const n0 = t0()
_applyVShow(n0, () => (_ctx.ok))
return n0 return n0
} }
}, true) }, true)

View File

@ -26,6 +26,7 @@ import { transformVFor } from './transforms/vFor'
import { transformComment } from './transforms/transformComment' import { transformComment } from './transforms/transformComment'
import { transformSlotOutlet } from './transforms/transformSlotOutlet' import { transformSlotOutlet } from './transforms/transformSlotOutlet'
import { transformVSlot } from './transforms/vSlot' import { transformVSlot } from './transforms/vSlot'
import { transformTransition } from './transforms/transformTransition'
import type { HackOptions } from './ir' import type { HackOptions } from './ir'
export { wrapTemplate } from './transforms/utils' export { wrapTemplate } from './transforms/utils'
@ -54,6 +55,7 @@ export function compile(
extend({}, resolvedOptions, { extend({}, resolvedOptions, {
nodeTransforms: [ nodeTransforms: [
...nodeTransforms, ...nodeTransforms,
...(__DEV__ ? [transformTransition] : []),
...(options.nodeTransforms || []), // user transforms ...(options.nodeTransforms || []), // user transforms
], ],
directiveTransforms: extend( directiveTransforms: extend(

View File

@ -0,0 +1,15 @@
import type { NodeTransform } from '@vue/compiler-vapor'
import { ElementTypes, NodeTypes } from '@vue/compiler-core'
import { isTransitionTag } from '../utils'
import { postTransformTransition } from '@vue/compiler-dom'
export const transformTransition: NodeTransform = (node, context) => {
if (
node.type === NodeTypes.ELEMENT &&
node.tagType === ElementTypes.COMPONENT
) {
if (isTransitionTag(node.tag)) {
return postTransformTransition(node, context.options.onError)
}
}
}