]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
chore: Merge branch 'vapor' into edison/testVapor
authordaiwei <daiwei521@126.com>
Fri, 20 Jun 2025 01:35:45 +0000 (09:35 +0800)
committerdaiwei <daiwei521@126.com>
Fri, 20 Jun 2025 01:35:45 +0000 (09:35 +0800)
16 files changed:
1  2 
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts
packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
packages/compiler-vapor/src/compile.ts
packages/compiler-vapor/src/generators/component.ts
packages/compiler-vapor/src/ir/index.ts
packages/compiler-vapor/src/transform.ts
packages/compiler-vapor/src/transforms/transformElement.ts
packages/compiler-vapor/src/transforms/transformSlotOutlet.ts
packages/compiler-vapor/src/transforms/utils.ts
packages/compiler-vapor/src/transforms/vSlot.ts
packages/runtime-core/src/index.ts
packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts
packages/runtime-vapor/src/apiCreateDynamicComponent.ts
packages/runtime-vapor/src/vdomInterop.ts

index 64d9abf1b88bab92350848f7c6e585937d974e8f,18f0139ab568e4f082c88241bca7f6a87b53bb98..a1a6e5085dc2028c381922f9cd3a50c91dcb9f3d
@@@ -54,9 -52,7 +54,8 @@@ export interface BlockIRNode extends Ba
    tempId: number
    effect: IREffect[]
    operation: OperationNode[]
-   expressions: SimpleExpressionNode[]
    returns: number[]
 +  hasDeferredVShow: boolean
  }
  
  export interface RootIRNode {
index 41a65b9dd36590c1135acb89f9c4d37f56fcbdae,f7d0594fe58151ac73eae39833d1978343d7116b..30fdcbc414655ad3574cebf006829f02a51e607d
@@@ -30,9 -29,7 +30,8 @@@ export const newBlock = (node: BlockIRN
    effect: [],
    operation: [],
    returns: [],
-   expressions: [],
    tempId: 0,
 +  hasDeferredVShow: false,
  })
  
  export function wrapTemplate(node: ElementNode, dirs: string[]): TemplateNode {
index df8fb6a5a6a3c2157b4d230105debac2a2ba3185,3e78913a23ef2fdd40c13a8fcb92a78e43225cb5..05790825a258cf1e700f67a5b680a6da91a4125b
@@@ -23,12 -23,8 +23,13 @@@ import 
    type SlotBlockIRNode,
    type VaporDirectiveNode,
  } from '../ir'
 -import { findDir, resolveExpression } from '../utils'
 +import {
 +  findDir,
 +  findProp,
 +  isTransitionNode,
 +  resolveExpression,
 +} from '../utils'
+ import { markNonTemplate } from './transformText'
  
  export const transformVSlot: NodeTransform = (node, context) => {
    if (node.type !== NodeTypes.ELEMENT) return
@@@ -71,24 -67,23 +72,33 @@@ function transformComponentSlot
  ) {
    const { children } = node
    const arg = dir && dir.arg
-   const nonSlotTemplateChildren = children.filter(
-     n =>
-       isNonWhitespaceContent(node) &&
-       !(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot)),
-   )
 -
+   // whitespace: 'preserve'
+   const emptyTextNodes: TemplateChildNode[] = []
+   const nonSlotTemplateChildren = children.filter(n => {
+     if (isNonWhitespaceContent(n)) {
+       return !(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot))
+     } else {
+       emptyTextNodes.push(n)
+     }
+   })
+   if (!nonSlotTemplateChildren.length) {
+     emptyTextNodes.forEach(n => {
+       markNonTemplate(n, context)
+     })
+   }
  
 -  const [block, onExit] = createSlotBlock(node, dir, context)
 +  let slotKey
-   if (isTransitionNode(node)) {
++  if (isTransitionNode(node) && nonSlotTemplateChildren.length) {
 +    const keyProp = findProp(
 +      nonSlotTemplateChildren[0] as ElementNode,
 +      'key',
 +    ) as VaporDirectiveNode
 +    if (keyProp) {
 +      slotKey = keyProp.exp
 +    }
 +  }
 +
 +  const [block, onExit] = createSlotBlock(node, dir, context, slotKey)
  
    const { slots } = context
  
index 889ab132b6481e1d8c374f34e0ef41405ae8711b,1ed6f21df7769a695a0643a6c27c38eaeaf30eef..cbbe04772cbd74903418e3074a4e2d686fab7f98
@@@ -562,11 -557,7 +562,15 @@@ export { startMeasure, endMeasure } fro
   * @internal
   */
  export { initFeatureFlags } from './featureFlags'
 +/**
 + * @internal
 + */
 +export { performTransitionEnter, performTransitionLeave } from './renderer'
 +/**
 + * @internal
 + */
 +export { ensureVaporSlotFallback } from './helpers/renderSlot'
+ /**
+  * @internal
+  */
+ export { createInternalObject } from './internalObject'
index 87dc922ddf666b437ec9c1d247d4c0db57256a90,e912af2851ab4719e0a198a51289a2f950f67626..99de869e21698937bc49d88c265415bc1b12966f
@@@ -79,4 -80,34 +80,52 @@@ describe('api: createDynamicComponent'
      mount()
      expect(html()).toBe('<div><button>hi</button></div>')
    })
+   test('switch dynamic component children', async () => {
+     const CompA = defineVaporComponent({
+       setup() {
+         return template('<div>A</div>')()
+       },
+     })
+     const CompB = defineVaporComponent({
+       setup() {
+         return template('<div>B</div>')()
+       },
+     })
+     const current = shallowRef(CompA)
+     const { html } = define({
+       setup() {
+         const t1 = template('<div></div>')
+         const n2 = t1() as any
+         setInsertionState(n2)
+         createDynamicComponent(() => current.value)
+         return n2
+       },
+     }).render()
+     expect(html()).toBe('<div><div>A</div><!--dynamic-component--></div>')
+     current.value = CompB
+     await nextTick()
+     expect(html()).toBe('<div><div>B</div><!--dynamic-component--></div>')
+   })
++
++  test('render fallback with insertionState', async () => {
++    const { html, mount } = define({
++      setup() {
++        const html = ref('hi')
++        const n1 = template('<div></div>', true)() as any
++        setInsertionState(n1)
++        const n0 = createComponentWithFallback(
++          resolveDynamicComponent('button') as any,
++        ) as any
++        renderEffect(() => setHtml(n0, html.value))
++        return n1
++      },
++    }).create()
++
++    mount()
++    expect(html()).toBe('<div><button>hi</button></div>')
++  })
  })
index 9bd4993f8372a61f85ee838957d5e3915e3b2631,945e0f38d8729b9f97fb2ef42296d8dcf62b453f..522fd43c232af58180563fcd685c388f24729b3d
@@@ -9,8 -9,7 +9,8 @@@ import 
    insertionParent,
    resetInsertionState,
  } from './insertionState'
- import { isHydrating } from './dom/hydration'
 +import { DYNAMIC_COMPONENT_ANCHOR_LABEL } from '@vue/shared'
+ import { isHydrating, locateHydrationNode } from './dom/hydration'
  
  export function createDynamicComponent(
    getter: () => any,
  ): VaporFragment {
    const _insertionParent = insertionParent
    const _insertionAnchor = insertionAnchor
-   if (!isHydrating) resetInsertionState()
+   if (isHydrating) {
+     locateHydrationNode()
+   } else {
+     resetInsertionState()
+   }
  
 -  const frag = __DEV__
 -    ? new DynamicFragment('dynamic-component')
 -    : new DynamicFragment()
 +  const frag =
 +    isHydrating || __DEV__
 +      ? new DynamicFragment(DYNAMIC_COMPONENT_ANCHOR_LABEL)
 +      : new DynamicFragment()
    renderEffect(() => {
      const value = getter()
 +    const appContext =
 +      (currentInstance && currentInstance.appContext) || emptyContext
      frag.update(
        () =>
          createComponentWithFallback(
index 236836b4c97ea3993eb6ee384ab12610bc4332b9,b916a2c8ebb0106b1d7542db2a83c17005c2649e..e80e359427c8f6994b30546536925619b4102191
@@@ -2,27 -2,20 +2,29 @@@ import 
    type App,
    type ComponentInternalInstance,
    type ConcreteComponent,
 +  type HydrationRenderer,
    MoveType,
    type Plugin,
 +  type RendererElement,
    type RendererInternals,
 +  type RendererNode,
    type ShallowRef,
    type Slots,
 +  type TransitionHooks,
    type VNode,
    type VaporInteropInterface,
+   createInternalObject,
    createVNode,
    currentInstance,
 +  ensureHydrationRenderer,
    ensureRenderer,
 +  ensureVaporSlotFallback,
+   isEmitListener,
 +  isVNode,
    onScopeDispose,
    renderSlot,
 +  setTransitionHooks as setVNodeTransitionHooks,
 +  shallowReactive,
    shallowRef,
    simpleSetCurrentInstance,
  } from '@vue/runtime-dom'
@@@ -229,16 -163,15 +231,23 @@@ function createVDOMComponent
  
    // overwrite how the vdom instance handles props
    vnode.vi = (instance: ComponentInternalInstance) => {
 -    instance.props = wrapper.props
 +    // Ensure props are shallow reactive to align with VDOM behavior.
 +    // This enables direct watching of props and prevents DEV warnings.
 +    //
 +    // Example:
 +    // const props = defineProps(...)
 +    // watch(props, () => { ... }) // props must be reactive for this to work
 +    //
 +    // Without reactivity, Vue will warn in DEV about non-reactive watch sources
 +    instance.props = shallowReactive(wrapper.props)
-     instance.attrs = wrapper.attrs
+     const attrs = (instance.attrs = createInternalObject())
+     for (const key in wrapper.attrs) {
+       if (!isEmitListener(instance.emitsOptions, key)) {
+         attrs[key] = wrapper.attrs[key]
+       }
+     }
      instance.slots =
        wrapper.slots === EMPTY_OBJ
          ? EMPTY_OBJ