Skip to content
Draft
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