Skip to content
Prev Previous commit
Next Next commit
some improvements and added missing types on VueWrapper
  • Loading branch information
pikax committed Nov 15, 2023
commit 2e0e1a655926f611785ec6fedaad31dbcdfa5368
55 changes: 27 additions & 28 deletions src/mount.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { ComponentPublicInstance, DefineComponent, VNode } from 'vue'
import type {
ComponentExposed,
import {
ComponentPublicInstance,
DefineComponent,
VNode,
ComponentProps,
ComponentSlots
} from 'vue-component-type-helpers'
ComponentInstance
} from 'vue'
import type { ComponentSlots } from 'vue-component-type-helpers'
import { createInstance } from './createInstance'
import { MountingOptions } from './types'
import { trackInstance } from './utils/autoUnmount'
Expand All @@ -18,10 +20,10 @@ type WithArray<T> = T | T[]

type ComponentData<T> = T extends { data?(...args: any): infer D } ? D : {}

export type ComponentMountingOptions<
T,
P extends ComponentProps<T> = ComponentProps<T>
> = Omit<MountingOptions<P, ComponentData<T>>, 'slots'> & {
export type ComponentMountingOptions<T, P> = Omit<
MountingOptions<P, ComponentData<T>>,
'slots'
> & {
slots?: {
[K in keyof ComponentSlots<T>]: WithArray<
| ShimSlotReturnType<ComponentSlots<T>[K]>
Expand All @@ -33,28 +35,25 @@ export type ComponentMountingOptions<
}
} & Record<string, unknown>

// defineComponent
export function mount<
T,
C = T extends ((...args: any) => any) | (new (...args: any) => any)
? T
: T extends { props?: infer Props }
? DefineComponent<
Props extends Readonly<(infer PropNames)[]> | (infer PropNames)[]
? { [key in PropNames extends string ? PropNames : string]?: any }
: Props
>
: DefineComponent,
P extends ComponentProps<C> = ComponentProps<C>
T extends DefineComponent<
PropsOrOptions,
any,
any,
any,
any,
any,
any,
any,
any,
any
>,
PropsOrOptions
>(
originalComponent: T,
options?: ComponentMountingOptions<C, P>
): VueWrapper<
ComponentProps<C> & ComponentData<C> & ComponentExposed<C>,
ComponentPublicInstance<
ComponentProps<C>,
ComponentData<C> & ComponentExposed<C> & Omit<P, keyof ComponentProps<C>>
>
>
options?: ComponentMountingOptions<T, ComponentProps<T>>
): VueWrapper<ComponentInstance<T>>

// implementation
export function mount(
Expand Down
45 changes: 33 additions & 12 deletions src/renderToString.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,44 @@
import { renderToString as baseRenderToString } from '@vue/server-renderer'
import { DefineComponent } from 'vue'
import { ComponentProps, DefineComponent } from 'vue'
import { createInstance } from './createInstance'
import { ComponentMountingOptions } from './mount'
import { RenderMountingOptions } from './types'

// export function renderToString<
// T,
// C = T extends ((...args: any) => any) | (new (...args: any) => any)
// ? T
// : T extends { props?: infer Props }
// ? DefineComponent<
// Props extends Readonly<(infer PropNames)[]> | (infer PropNames)[]
// ? { [key in PropNames extends string ? PropNames : string]?: any }
// : Props
// >
// : DefineComponent
// >(
// originalComponent: T,
// options?: ComponentMountingOptions<C> &
// Pick<RenderMountingOptions<any>, 'attachTo'>
// ): Promise<string>

// defineComponent
export function renderToString<
T,
C = T extends ((...args: any) => any) | (new (...args: any) => any)
? T
: T extends { props?: infer Props }
? DefineComponent<
Props extends Readonly<(infer PropNames)[]> | (infer PropNames)[]
? { [key in PropNames extends string ? PropNames : string]?: any }
: Props
>
: DefineComponent
T extends DefineComponent<
PropsOrOptions,
any,
any,
any,
any,
any,
any,
any,
any,
any
>,
PropsOrOptions
>(
originalComponent: T,
options?: ComponentMountingOptions<C> &
options?: ComponentMountingOptions<T, ComponentProps<T>> &
Pick<RenderMountingOptions<any>, 'attachTo'>
): Promise<string>

Expand Down
48 changes: 38 additions & 10 deletions src/vueWrapper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { nextTick, App, ComponentPublicInstance, VNode } from 'vue'
import {
nextTick,
App,
ComponentPublicInstance,
VNode,
ExtractComponentEmits
} from 'vue'

import { config } from './config'
import domEvents from './constants/dom-events'
Expand Down Expand Up @@ -72,9 +78,24 @@ function createVMProxy<T extends ComponentPublicInstance>(
})
}

type ResolveComponentEmitKeys<T> = keyof ResolveEmitRecord<T>

type ResolveEmitRecord<T> = ExtractComponentEmits<T> extends infer E
? [E] extends [Array<infer EE extends string>]
? Record<EE, any[]>
: {
[K in keyof E]: (E[K] extends (...args: infer Args) => any
? Args extends { length: 0 }
? void
: Args extends { length: 1 }
? Args[0]
: Args
: void)[]
}
: never

export class VueWrapper<
VM = unknown,
T extends ComponentPublicInstance = VM & ComponentPublicInstance
T extends ComponentPublicInstance = ComponentPublicInstance
> extends BaseWrapper<Node> {
private readonly componentVM: T
private readonly rootVM: ComponentPublicInstance | undefined | null
Expand Down Expand Up @@ -226,11 +247,11 @@ export class VueWrapper<
return selector ? props[selector] : props
}

emitted<T = unknown>(): Record<string, T[]>
emitted<T = unknown[]>(eventName: string): undefined | T[]
emitted<T = unknown>(
eventName?: string
): undefined | T[] | Record<string, T[]> {
emitted(): ResolveEmitRecord<T>
emitted<E extends ResolveComponentEmitKeys<T>>(
eventName: E
): undefined | ResolveEmitRecord<T>[E]
emitted(eventName?: string) {
return emitted(this.vm, eventName)
}

Expand All @@ -239,7 +260,7 @@ export class VueWrapper<
return domWrapper.isVisible()
}

setData(data: Record<string, unknown>): Promise<void> {
setData(data: Partial<T['$data']>): Promise<void> {
mergeDeep(this.componentVM.$data, data)
return nextTick()
}
Expand All @@ -253,7 +274,14 @@ export class VueWrapper<
return nextTick()
}

setValue(value: unknown, prop?: string): Promise<void> {
setValue<
V extends T['$props'] extends { modelValue?: infer MV } ? MV : never
>(value: V): Promise<void>
setValue<P extends keyof T['$props']>(
value: T['$props'][P],
prop: P
): Promise<void>
setValue(value: unknown, prop?: unknown): Promise<void> {
const propEvent = prop || 'modelValue'
this.vm.$emit(`update:${propEvent}`, value)
return this.vm.$nextTick()
Expand Down