Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(runtime-vapor): component emits #42

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e2edd62
wip: component props
ubugeeei Dec 4, 2023
953b99d
Merge branch 'main' of https://github.com/vuejs/core-vapor into feat/…
ubugeeei Dec 7, 2023
79aa1d9
refactor(runtime-vapor): remove dead console.log
ubugeeei Dec 7, 2023
b6e699a
feat(runtime-vapor): componentPublicInstance
ubugeeei Dec 7, 2023
2460435
chore(runtime-vapor): fix type import
ubugeeei Dec 7, 2023
d6ffd71
chore(runtime-vapor): fix conflict
ubugeeei Dec 8, 2023
551e5c1
chore(runtime-vapor): remove props type check option
ubugeeei Dec 8, 2023
7bcb160
chore(runtime-vapor): remove dead props
ubugeeei Dec 8, 2023
2de519f
Merge branch 'main' of https://github.com/vuejs/core-vapor into feat/…
ubugeeei Dec 8, 2023
4b55722
feat(runtime-vapor): component emits
ubugeeei Dec 8, 2023
123108f
refactor
sxzz Dec 8, 2023
a98b5c0
chore: fix conflict
ubugeeei Dec 9, 2023
8bf0adb
Merge branch 'main' of https://github.com/vuejs/core-vapor into feat/…
ubugeeei Dec 9, 2023
3a28a20
Merge branch 'feat/component-props' of https://github.com/Ubugeeei/vu…
ubugeeei Dec 9, 2023
d28d6d7
chore: fix conflict
ubugeeei Dec 10, 2023
461c283
feat(vapor-playground): emit once
ubugeeei Dec 10, 2023
095c3db
chore(runtime-vapor): add todo comment
ubugeeei Dec 10, 2023
64a50c0
feat(vapor-playground): props default
ubugeeei Dec 10, 2023
7882f4b
Merge branch 'main' of https://github.com/vuejs/core-vapor into feat/…
ubugeeei Dec 11, 2023
7c56bb9
fix: conflict
ubugeeei Dec 13, 2023
d8bd68a
fix: define getter of emit handler
ubugeeei Dec 13, 2023
3d7f05a
fix: conflict
ubugeeei Dec 15, 2023
1aac567
Merge branch 'main' of https://github.com/vuejs/core-vapor into feat/…
ubugeeei Dec 16, 2023
cb645d9
fix: conflict
ubugeeei Dec 23, 2023
3eb5f40
fix: conflict
ubugeeei Dec 25, 2023
7931cf6
fix: conflict
ubugeeei Dec 25, 2023
edc792d
Merge branch 'main' of https://github.com/vuejs/core-vapor into feat/…
ubugeeei Dec 31, 2023
76418ca
fix: lint
ubugeeei Dec 31, 2023
3c1d342
Merge branch 'main' of https://github.com/vuejs/core-vapor into feat/…
ubugeeei Jan 3, 2024
47780c4
Merge branch 'main' of https://github.com/Ubugeeei/vuejs-core into fe…
ubugeeei Jan 4, 2024
610450d
chore: update readme (Codes Copied From `runtime-core`)
ubugeeei Jan 4, 2024
4dca463
Merge branch 'main' of https://github.com/Ubugeeei/vuejs-core into fe…
ubugeeei Jan 6, 2024
41cd245
Merge branch 'main' into feat/component-emits
ubugeeei Jan 8, 2024
26c958d
Merge branch 'main' of https://github.com/vuejs/core-vapor into feat/…
ubugeeei Jan 21, 2024
f6e71af
Merge branch 'main' of https://github.com/vuejs/core-vapor into feat/…
ubugeeei Jan 24, 2024
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: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ The code provided here is a duplicate from `runtime-core` as Vapor cannot import

- packages/runtime-vapor/src/apiWatch.ts
- packages/runtime-vapor/src/component.ts
- packages/runtime-vapor/src/componentEmits.ts
- packages/runtime-vapor/src/componentProps.ts
- packages/runtime-vapor/src/componentPublicInstance.ts
- packages/runtime-vapor/src/enums.ts
- packages/runtime-vapor/src/errorHandling.ts
- packages/runtime-vapor/src/scheduler.ts
Expand Down
11 changes: 5 additions & 6 deletions packages/reactivity/src/ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,12 +501,11 @@ export type ShallowUnwrapRef<T> = {

type DistrubuteRef<T> = T extends Ref<infer V> ? V : T

export type UnwrapRef<T> =
T extends ShallowRef<infer V>
? V
: T extends Ref<infer V>
? UnwrapRefSimple<V>
: UnwrapRefSimple<T>
export type UnwrapRef<T> = T extends ShallowRef<infer V>
? V
: T extends Ref<infer V>
? UnwrapRefSimple<V>
: UnwrapRefSimple<T>

export type UnwrapRefSimple<T> = T extends
| Function
Expand Down
38 changes: 18 additions & 20 deletions packages/runtime-core/src/componentEmits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,30 +54,28 @@ export type EmitsToProps<T extends EmitsOptions> = T extends string[]
}
: {}

export type ShortEmitsToObject<E> =
E extends Record<string, any[]>
? {
[K in keyof E]: (...args: E[K]) => any
}
: E
export type ShortEmitsToObject<E> = E extends Record<string, any[]>
? {
[K in keyof E]: (...args: E[K]) => any
}
: E

export type EmitFn<
Options = ObjectEmitsOptions,
Event extends keyof Options = keyof Options,
> =
Options extends Array<infer V>
? (event: V, ...args: any[]) => void
: {} extends Options // if the emit is empty object (usually the default value for emit) should be converted to function
? (event: string, ...args: any[]) => void
: UnionToIntersection<
{
[key in Event]: Options[key] extends (...args: infer Args) => any
? (event: key, ...args: Args) => void
: Options[key] extends any[]
? (event: key, ...args: Options[key]) => void
: (event: key, ...args: any[]) => void
}[Event]
>
> = Options extends Array<infer V>
? (event: V, ...args: any[]) => void
: {} extends Options // if the emit is empty object (usually the default value for emit) should be converted to function
? (event: string, ...args: any[]) => void
: UnionToIntersection<
{
[key in Event]: Options[key] extends (...args: infer Args) => any
? (event: key, ...args: Args) => void
: Options[key] extends any[]
? (event: key, ...args: Options[key]) => void
: (event: key, ...args: any[]) => void
}[Event]
>

export function emit(
instance: ComponentInternalInstance,
Expand Down
46 changes: 22 additions & 24 deletions packages/runtime-core/src/componentPublicInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,36 +84,34 @@ type IsDefaultMixinComponent<T> = T extends ComponentOptionsMixin
: false
: false

type MixinToOptionTypes<T> =
T extends ComponentOptionsBase<
infer P,
infer B,
infer D,
infer C,
infer M,
infer Mixin,
infer Extends,
any,
any,
infer Defaults,
any,
any,
any
>
? OptionTypesType<P & {}, B & {}, D & {}, C & {}, M & {}, Defaults & {}> &
IntersectionMixin<Mixin> &
IntersectionMixin<Extends>
: never
type MixinToOptionTypes<T> = T extends ComponentOptionsBase<
infer P,
infer B,
infer D,
infer C,
infer M,
infer Mixin,
infer Extends,
any,
any,
infer Defaults,
any,
any,
any
>
? OptionTypesType<P & {}, B & {}, D & {}, C & {}, M & {}, Defaults & {}> &
IntersectionMixin<Mixin> &
IntersectionMixin<Extends>
: never

// ExtractMixin(map type) is used to resolve circularly references
type ExtractMixin<T> = {
Mixin: MixinToOptionTypes<T>
}[T extends ComponentOptionsMixin ? 'Mixin' : never]

export type IntersectionMixin<T> =
IsDefaultMixinComponent<T> extends true
? OptionTypesType
: UnionToIntersection<ExtractMixin<T>>
export type IntersectionMixin<T> = IsDefaultMixinComponent<T> extends true
? OptionTypesType
: UnionToIntersection<ExtractMixin<T>>

export type UnwrapMixinsType<
T,
Expand Down
50 changes: 42 additions & 8 deletions packages/runtime-vapor/src/component.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
import type { Data } from '@vue/shared'
import { EMPTY_OBJ } from '@vue/shared'
import { EffectScope } from '@vue/reactivity'

import { EMPTY_OBJ } from '@vue/shared'
import type { Block } from './render'
import type { DirectiveBinding } from './directive'
import {
type ComponentPropsOptions,
type NormalizedPropsOptions,
normalizePropsOptions,
import type {
ComponentPropsOptions,
NormalizedPropsOptions,
} from './componentProps'
import { normalizePropsOptions } from './componentProps'

import type { Data } from '@vue/shared'
import type { EmitFn, EmitsOptions, ObjectEmitsOptions } from './componentEmits'
import { emit, normalizeEmitsOptions } from './componentEmits'
import { VaporLifecycleHooks } from './enums'

export type Component = FunctionalComponent | ObjectComponent

export type SetupFn = (props: any, ctx: any) => Block | Data
export type FunctionalComponent = SetupFn & {
props: ComponentPropsOptions
emits: EmitsOptions
render(ctx: any): Block
}
export interface ObjectComponent {
props: ComponentPropsOptions
emits: EmitsOptions
setup?: SetupFn
render(ctx: any): Block
}
Expand All @@ -36,14 +40,25 @@ export interface ComponentInternalInstance {
container: ParentNode
block: Block | null
scope: EffectScope

// conventional vnode.type
component: FunctionalComponent | ObjectComponent
rawProps: Data

// normalized options
propsOptions: NormalizedPropsOptions
emitsOptions: ObjectEmitsOptions | null

parent: ComponentInternalInstance | null

// TODO: type
proxy: Data | null

// state
props: Data
setupState: Data
emit: EmitFn
emitted: Record<string, boolean> | null
refs: Data
metadata: WeakMap<Node, ElementMetadata>

Expand All @@ -52,11 +67,13 @@ export interface ComponentInternalInstance {
/** directives */
dirs: Map<Node, DirectiveBinding[]>

// TODO: registory of provides, appContext, ...

// lifecycle
isMounted: boolean
isUnmounted: boolean
isUpdating: boolean
// TODO: registory of provides, lifecycles, ...

/**
* @internal
*/
Expand Down Expand Up @@ -139,19 +156,30 @@ export const unsetCurrentInstance = () => {
let uid = 0
export const createComponentInstance = (
component: ObjectComponent | FunctionalComponent,
rawProps: Data,
): ComponentInternalInstance => {
const instance: ComponentInternalInstance = {
uid: uid++,
block: null,
container: null!, // set on mountComponent
scope: new EffectScope(true /* detached */)!,
component,
rawProps,

// TODO: registory of parent
parent: null,

// resolved props and emits options
propsOptions: normalizePropsOptions(component),

emitsOptions: normalizeEmitsOptions(component),

// emit
emit: null!, // to be set immediately
emitted: null,

proxy: null,

// emitsOptions: normalizeEmitsOptions(type, appContext), // TODO:

// state
Expand All @@ -163,11 +191,14 @@ export const createComponentInstance = (

dirs: new Map(),

// TODO: registory of provides, appContext, ...

// lifecycle

isMounted: false,
isUnmounted: false,
isUpdating: false,
// TODO: registory of provides, appContext, lifecycles, ...

/**
* @internal
*/
Expand Down Expand Up @@ -225,5 +256,8 @@ export const createComponentInstance = (
*/
// [VaporLifecycleHooks.SERVER_PREFETCH]: null,
}

instance.emit = emit.bind(null, instance)

return instance
}
95 changes: 95 additions & 0 deletions packages/runtime-vapor/src/componentEmits.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// NOTE: runtime-core/src/componentEmits.ts

import {
type UnionToIntersection,
camelize,
extend,
hyphenate,
isArray,
isFunction,
toHandlerKey,
} from '@vue/shared'
import type { Component, ComponentInternalInstance } from './component'

export type ObjectEmitsOptions = Record<
string,
((...args: any[]) => any) | null // TODO: call validation?
>

export type EmitsOptions = ObjectEmitsOptions | string[]

export type EmitFn<
Options = ObjectEmitsOptions,
Event extends keyof Options = keyof Options,
> = Options extends Array<infer V>
? (event: V, ...args: any[]) => void
: {} extends Options // if the emit is empty object (usually the default value for emit) should be converted to function
? (event: string, ...args: any[]) => void
: UnionToIntersection<
{
[key in Event]: Options[key] extends (...args: infer Args) => any
? (event: key, ...args: Args) => void
: (event: key, ...args: any[]) => void
}[Event]
>

export function emit(
instance: ComponentInternalInstance,
event: string,
...rawArgs: any[]
) {
if (instance.isUnmounted) return
const props = instance.rawProps // TODO: raw props

let args = rawArgs

// TODO: modelListener

let handlerName
let handler =
props[(handlerName = toHandlerKey(event))] ||
// also try camelCase event handler (#2249)
props[(handlerName = toHandlerKey(camelize(event)))]
// for v-model update:xxx events, also trigger kebab-case equivalent
// for props passed via kebab-case
if (!handler) {
handler = props[(handlerName = toHandlerKey(hyphenate(event)))]
}

if (handler && isFunction(handler)) {
// TODO: callWithAsyncErrorHandling
handler(...args)
}

const onceHandler = props[handlerName + `Once`]
if (onceHandler) {
if (!instance.emitted) {
instance.emitted = {}
} else if (instance.emitted[handlerName]) {
return
}

if (isFunction(onceHandler)) {
instance.emitted[handlerName] = true
// TODO: callWithAsyncErrorHandling
onceHandler(...args)
}
}
}

export function normalizeEmitsOptions(
comp: Component,
): ObjectEmitsOptions | null {
// TODO: caching?

const raw = comp.emits
let normalized: ObjectEmitsOptions = {}

if (isArray(raw)) {
raw.forEach((key) => (normalized[key] = null))
} else {
extend(normalized, raw)
}

return normalized
}
1 change: 1 addition & 0 deletions packages/runtime-vapor/src/componentProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export function initProps(
if (rawProps) {
for (let key in rawProps) {
// key, ref are reserved and never passed down
// TODO: remove vnode
if (isReservedProp(key)) {
continue
}
Expand Down
24 changes: 24 additions & 0 deletions packages/runtime-vapor/src/componentPublicInstance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { hasOwn } from '@vue/shared'
import type { ComponentInternalInstance } from './component'

export interface ComponentRenderContext {
[key: string]: any
_: ComponentInternalInstance
}

export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
get({ _: instance }: ComponentRenderContext, key: string) {
let normalizedProps
const { setupState, props } = instance
if (hasOwn(setupState, key)) {
return setupState[key]
} else if (
(normalizedProps = instance.propsOptions[0]) &&
hasOwn(normalizedProps, key)
) {
return props![key]
}
},
}

// TODO: publicPropertiesMap
Loading