Skip to content
This repository was archived by the owner on Jul 19, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/runtime-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export {

// For getting a hold of the internal instance in setup() - useful for advanced
// plugins
export { getCurrentInstance } from './component'
export { getCurrentInstance, getExposeProxy } from './component'

// For raw render function users
export { h } from './h'
Expand Down
61 changes: 59 additions & 2 deletions packages/runtime-vapor/src/component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { EffectScope, Ref, ref } from '@vue/reactivity'
import { EffectScope, isRef, Ref, ref } from '@vue/reactivity'

import { EMPTY_OBJ } from '@vue/shared'
import { EMPTY_OBJ, isArray } from '@vue/shared'
import { Block } from './render'
import { type DirectiveBinding } from './directive'
import {
Expand All @@ -11,6 +11,7 @@ import {

import type { Data } from '@vue/shared'
import { VaporLifecycleHooks } from './apiLifecycle'
import { warn } from '@vue/runtime-core'

export type Component = FunctionalComponent | ObjectComponent

Expand Down Expand Up @@ -42,6 +43,10 @@ export interface ComponentInternalInstance {
props: Data
setupState: Data

// exposed properties via expose()
exposed: Record<string, any> | null
exposeProxy: Record<string, any> | null

/** directives */
dirs: Map<Node, DirectiveBinding[]>

Expand Down Expand Up @@ -123,6 +128,54 @@ export const unsetCurrentInstance = () => {
currentInstance = null
}

// TODO: Maybe it can be reused
// TODO: https://github.com/vuejs/core/blob/04d2c05054c26b02fbc1d84839b0ed5cd36455b6/packages/runtime-core/src/component.ts#L186C1-L186C1
export type SetupContext = {
expose: (exposed?: Record<string, any>) => void
}

// TODO: Maybe it can be reused
// TODO: https://github.com/vuejs/core/blob/04d2c05054c26b02fbc1d84839b0ed5cd36455b6/packages/runtime-core/src/component.ts#L983
export function createSetupContext(
instance: ComponentInternalInstance,
): SetupContext {
const expose: SetupContext['expose'] = (exposed) => {
if (__DEV__) {
if (instance.exposed) {
warn(`expose() should be called only once per setup().`)
}
if (exposed != null) {
let exposedType: string = typeof exposed
if (exposedType === 'object') {
if (isArray(exposed)) {
exposedType = 'array'
} else if (isRef(exposed)) {
exposedType = 'ref'
}
}
if (exposedType !== 'object') {
warn(
`expose() should be passed a plain object, received ${exposedType}.`,
)
}
}
}
instance.exposed = exposed || {}
}

if (__DEV__) {
// We use getters in dev in case libs like test-utils overwrite instance
// properties (overwrites should not be done in prod)
return Object.freeze({
expose,
})
} else {
return {
expose,
}
}
}

let uid = 0
export const createComponentInstance = (
component: ObjectComponent | FunctionalComponent,
Expand All @@ -145,6 +198,10 @@ export const createComponentInstance = (
props: EMPTY_OBJ,
setupState: EMPTY_OBJ,

// exposed properties via expose()
exposed: null,
exposeProxy: null,

dirs: new Map(),

// lifecycle
Expand Down
7 changes: 6 additions & 1 deletion packages/runtime-vapor/src/directive.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { isFunction } from '@vue/shared'
import { currentInstance, type ComponentInternalInstance } from './component'
import { effect } from './scheduler'
import { getExposeProxy } from '@vue/runtime-core'

export type DirectiveModifiers<M extends string = string> = Record<M, boolean>

Expand Down Expand Up @@ -69,6 +70,10 @@ export function withDirectives<T extends Node>(
}

const instance = currentInstance
// TODO: Wait for the component instance type to be completed before deleting `any`
const instanceProxy =
(getExposeProxy(currentInstance as any) as ComponentInternalInstance) ||
currentInstance.proxy
if (!instance.dirs.has(node)) instance.dirs.set(node, [])
const bindings = instance.dirs.get(node)!

Expand All @@ -84,7 +89,7 @@ export function withDirectives<T extends Node>(

const binding: DirectiveBinding = {
dir,
instance,
instance: instanceProxy,
source,
value: null, // set later
oldValue: null,
Expand Down
3 changes: 2 additions & 1 deletion packages/runtime-vapor/src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
type Component,
type ComponentInternalInstance,
createComponentInstance,
createSetupContext,
setCurrentInstance,
unsetCurrentInstance,
} from './component'
Expand Down Expand Up @@ -42,7 +43,7 @@ export function mountComponent(
setCurrentInstance(instance)
const block = instance.scope.run(() => {
const { component, props } = instance
const ctx = { expose: () => {} }
const ctx = createSetupContext(instance)

const setupFn =
typeof component === 'function' ? component : component.setup
Expand Down