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

WIP: Support for vue type helpers changes #2242

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@
"react",
"react-dom"
]
},
"patchedDependencies": {
"@vue/[email protected]": "patches/@[email protected]"
}
}
}
417 changes: 417 additions & 0 deletions patches/@[email protected]

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

89 changes: 70 additions & 19 deletions src/baseWrapper.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { textContent } from './utils'
import type { TriggerOptions } from './createDomEvent'
import {
DefineComponentOptions,
DefineComponentFromOptions,
ComponentInjectOptions,
ComponentInstance,
ComponentInternalInstance,
ComponentOptions,
ComponentOptionsMixin,
ComponentPublicInstance,
ComputedOptions,
CreateComponentPublicInstance,
EmitsOptions,
FunctionalComponent,
MethodOptions,
SlotsType,
nextTick
} from 'vue'
import { createDOMEvent } from './createDomEvent'
Expand All @@ -19,6 +26,7 @@ import {
FindComponentSelector,
NameSelector,
RefSelector,
UnknownRenderedVue,
VueNode
} from './types'
import WrapperLike from './interfaces/wrapperLike'
Expand Down Expand Up @@ -108,16 +116,57 @@ export default abstract class BaseWrapper<ElementType extends Node>

// searching by string without specifying component results in WrapperLike object
findComponent<T extends never>(selector: string): WrapperLike

// Find Component Options aka plain object
findComponent<
Props,
RawBindings = any,
D = any,
C extends ComputedOptions = ComputedOptions,
M extends MethodOptions = MethodOptions
Props = {},
RawBindings = {},
D = {},
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = {},
EE extends string = string,
I extends ComponentInjectOptions = {},
II extends string = string,
S extends SlotsType = {},
Options = {}
>(
selector: ComponentOptions<Props, RawBindings, D, C, M>
): VueWrapper<CreateComponentPublicInstance<Props, RawBindings, D, C, M>>
selector: DefineComponentOptions<
Props,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
EE,
I,
II,
S,
Options
>
): VueWrapper<
Props extends DefinedComponent
? ComponentInstance<Props>
: DefineComponentFromOptions<
Props,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
EE,
I,
II,
S,
Options
>
>
findComponent<T extends ComponentOptions>(
selector: string
): VueWrapper<
Expand All @@ -129,21 +178,23 @@ export default abstract class BaseWrapper<ElementType extends Node>
infer M
>
? CreateComponentPublicInstance<Props, RawBindings, D, C, M>
: VueWrapper<CreateComponentPublicInstance>
: CreateComponentPublicInstance
>
// searching for component created via defineComponent results in VueWrapper of proper type
findComponent<T extends DefinedComponent>(
selector: T | Exclude<FindComponentSelector, FunctionalComponent>
): VueWrapper<InstanceType<T>>
// searching for functional component results in DOMWrapper
findComponent<T extends FunctionalComponent>(selector: T): DOMWrapper<Node>
findComponent<T extends FunctionalComponent>(
selector: string
): DOMWrapper<Element>

// searching by name or ref always results in VueWrapper
findComponent<T extends never>(
selector: NameSelector | RefSelector
): VueWrapper
): VueWrapper<UnknownRenderedVue>
// searching for component created via defineComponent results in VueWrapper of proper type
findComponent<T extends DefinedComponent>(
selector: T | Exclude<FindComponentSelector, FunctionalComponent>
): VueWrapper<ComponentInstance<T>>

findComponent<T extends ComponentPublicInstance>(
selector: T | FindComponentSelector
): VueWrapper<T>
Expand Down Expand Up @@ -185,7 +236,7 @@ export default abstract class BaseWrapper<ElementType extends Node>
findAllComponents<T extends never>(selector: string): WrapperLike[]
findAllComponents<T extends DefinedComponent>(
selector: T | Exclude<FindAllComponentsSelector, FunctionalComponent>
): VueWrapper<InstanceType<T>>[]
): VueWrapper<ComponentInstance<T>>[]
findAllComponents<T extends FunctionalComponent>(
selector: T
): DOMWrapper<Node>[]
Expand Down Expand Up @@ -290,17 +341,17 @@ export default abstract class BaseWrapper<ElementType extends Node>
}

getComponent<T extends never>(selector: string): Omit<WrapperLike, 'exists'>
// searching by name or ref always results in VueWrapper
getComponent<T extends never>(
selector: NameSelector | RefSelector
): Omit<VueWrapper<UnknownRenderedVue>, 'exists'>
getComponent<T extends DefinedComponent>(
selector: T | Exclude<FindComponentSelector, FunctionalComponent>
): Omit<VueWrapper<InstanceType<T>>, 'exists'>
): Omit<VueWrapper<ComponentInstance<T>>, 'exists'>
// searching for functional component results in DOMWrapper
getComponent<T extends FunctionalComponent>(
selector: T | string
): Omit<DOMWrapper<Element>, 'exists'>
// searching by name or ref always results in VueWrapper
getComponent<T extends never>(
selector: NameSelector | RefSelector
): Omit<VueWrapper, 'exists'>
getComponent<T extends ComponentPublicInstance>(
selector: T | FindComponentSelector
): Omit<VueWrapper<T>, 'exists'>
Expand Down
12 changes: 8 additions & 4 deletions src/interfaces/wrapperLike.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import {
RefSelector
} from '../types'
import { VueWrapper } from '../vueWrapper'
import { ComponentPublicInstance, FunctionalComponent } from 'vue'
import {
ComponentInstance,
ComponentPublicInstance,
FunctionalComponent
} from 'vue'
import type { DOMWrapper } from '../domWrapper'

export default interface WrapperLike {
Expand All @@ -35,7 +39,7 @@ export default interface WrapperLike {
findComponent<T extends never>(selector: string): WrapperLike
findComponent<T extends DefinedComponent>(
selector: T | Exclude<FindComponentSelector, FunctionalComponent>
): VueWrapper<InstanceType<T>>
): VueWrapper<ComponentInstance<T>>
findComponent<T extends FunctionalComponent>(
selector: T | string
): DOMWrapper<Element>
Expand All @@ -50,7 +54,7 @@ export default interface WrapperLike {
findAllComponents<T extends never>(selector: string): WrapperLike[]
findAllComponents<T extends DefinedComponent>(
selector: T | Exclude<FindAllComponentsSelector, FunctionalComponent>
): VueWrapper<InstanceType<T>>[]
): VueWrapper<ComponentInstance<T>>[]
findAllComponents<T extends FunctionalComponent>(
selector: string
): DOMWrapper<Element>[]
Expand Down Expand Up @@ -79,7 +83,7 @@ export default interface WrapperLike {
getComponent<T extends never>(selector: string): Omit<WrapperLike, 'exists'>
getComponent<T extends DefinedComponent>(
selector: T | Exclude<FindComponentSelector, FunctionalComponent>
): Omit<VueWrapper<InstanceType<T>>, 'exists'>
): Omit<VueWrapper<ComponentInstance<T>>, 'exists'>
// searching for functional component results in DOMWrapper
getComponent<T extends FunctionalComponent>(
selector: T | string
Expand Down
48 changes: 15 additions & 33 deletions src/mount.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
import { ComponentPublicInstance, DefineComponent, VNode } from 'vue'
import type {
ComponentExposed,
ComponentProps,
import {
ComponentPublicInstance,
DefineComponent,
VNode,
ComponentInstance,
ComponentSlots
} from 'vue-component-type-helpers'
} from 'vue'
import { createInstance } from './createInstance'
import { MountingOptions } from './types'
import { trackInstance } from './utils/autoUnmount'
import { VueWrapper } from './vueWrapper'
import { createVueWrapper } from './wrapperFactory'

type ShimSlotReturnType<T> = T extends (...args: infer P) => any
? (...args: P) => any
: never
import { ComponentPropsWithDefaultOptional } from 'vue'

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]>
| ComponentSlots<T>[K]
| string
| VNode
| (new () => any)
Expand All @@ -34,27 +32,11 @@ export type ComponentMountingOptions<
} & Record<string, unknown>

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<any, any, any, any, any, any, any, any, any, any>
>(
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, ComponentPropsWithDefaultOptional<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
7 changes: 6 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,9 @@ export type VueNode<T extends Node = Node> = T & {

export type VueElement = VueNode<Element>

export type DefinedComponent = new (...args: any[]) => any
export type DefinedComponent = Component

export type UnknownRenderedVue = {
$props: Record<string, any>
$data: Record<string, any>
}
Loading
Loading