@@ -6,17 +6,20 @@ import {
6
6
VNodeNormalizedChildren ,
7
7
transformVNodeArgs ,
8
8
reactive ,
9
+ FunctionalComponent ,
9
10
ComponentPublicInstance ,
10
11
ComponentOptionsWithObjectProps ,
11
12
ComponentOptionsWithArrayProps ,
12
13
ComponentOptionsWithoutProps ,
13
14
ExtractPropTypes ,
14
- Component
15
+ Component ,
16
+ AppConfig ,
17
+ VNodeProps
15
18
} from 'vue'
16
19
17
20
import { config } from './config'
18
21
import { GlobalMountOptions } from './types'
19
- import { mergeGlobalProperties , isString } from './utils'
22
+ import { mergeGlobalProperties } from './utils'
20
23
import { processSlot } from './utils/compileSlots'
21
24
import { createWrapper , VueWrapper } from './vue-wrapper'
22
25
import { attachEmitListener } from './emitMixin'
@@ -27,7 +30,6 @@ import {
27
30
MOUNT_PARENT_NAME
28
31
} from './constants'
29
32
import { stubComponents } from './stubs'
30
- import { parse } from '@vue/compiler-dom'
31
33
32
34
type Slot = VNode | string | { render : Function } | Function
33
35
@@ -53,6 +55,11 @@ type ExtractComponent<T> = T extends { new (): infer PublicInstance }
53
55
? PublicInstance
54
56
: any
55
57
58
+ // Functional component
59
+ export function mount < TestedComponent extends FunctionalComponent > (
60
+ originalComponent : TestedComponent ,
61
+ options ?: MountingOptions < any >
62
+ ) : VueWrapper < ComponentPublicInstance >
56
63
// Component declared with defineComponent
57
64
export function mount < TestedComponent extends ComponentPublicInstance > (
58
65
originalComponent : { new ( ) : TestedComponent } & Component ,
@@ -80,50 +87,68 @@ export function mount(
80
87
originalComponent : any ,
81
88
options ?: MountingOptions < any >
82
89
) : VueWrapper < any > {
83
- const component = { ...originalComponent }
90
+ // normalise the incoming component
91
+ const component =
92
+ typeof originalComponent === 'function'
93
+ ? defineComponent ( {
94
+ setup : ( _ , { attrs, slots } ) => ( ) =>
95
+ h ( originalComponent , attrs , slots )
96
+ } )
97
+ : { ...originalComponent }
84
98
85
99
const el = document . createElement ( 'div' )
86
100
el . id = MOUNT_ELEMENT_ID
87
101
88
102
if ( options ?. attachTo ) {
89
- const to = isString ( options . attachTo )
90
- ? document . querySelector ( options . attachTo )
91
- : options . attachTo
92
-
93
- if ( ! to ) {
94
- throw new Error (
95
- `Unable to find the element matching the selector ${ options . attachTo } given as the \`attachTo\` option`
96
- )
103
+ let to : Element | null
104
+ if ( typeof options . attachTo === 'string' ) {
105
+ to = document . querySelector ( options . attachTo )
106
+ if ( ! to ) {
107
+ throw new Error (
108
+ `Unable to find the element matching the selector ${ options . attachTo } given as the \`attachTo\` option`
109
+ )
110
+ }
111
+ } else {
112
+ to = options . attachTo
97
113
}
114
+
98
115
to . appendChild ( el )
99
116
}
100
117
101
118
// handle any slots passed via mounting options
102
119
const slots : VNodeNormalizedChildren =
103
120
options ?. slots &&
104
- Object . entries ( options . slots ) . reduce ( ( acc , [ name , slot ] ) => {
105
- // case of an SFC getting passed
106
- if ( typeof slot === 'object' && 'render' in slot ) {
107
- acc [ name ] = slot . render
108
- return acc
109
- }
121
+ Object . entries ( options . slots ) . reduce (
122
+ (
123
+ acc : { [ key : string ] : Function } ,
124
+ [ name , slot ] : [ string , Slot ]
125
+ ) : { [ key : string ] : Function } => {
126
+ // case of an SFC getting passed
127
+ if ( typeof slot === 'object' && 'render' in slot ) {
128
+ acc [ name ] = slot . render
129
+ return acc
130
+ }
110
131
111
- if ( typeof slot === 'function' ) {
112
- acc [ name ] = slot
113
- return acc
114
- }
132
+ if ( typeof slot === 'function' ) {
133
+ acc [ name ] = slot
134
+ return acc
135
+ }
115
136
116
- if ( typeof slot === 'object' ) {
117
- acc [ name ] = ( ) => slot
118
- return acc
119
- }
137
+ if ( typeof slot === 'object' ) {
138
+ acc [ name ] = ( ) => slot
139
+ return acc
140
+ }
141
+
142
+ if ( typeof slot === 'string' ) {
143
+ // slot is most probably a scoped slot string or a plain string
144
+ acc [ name ] = ( props : VNodeProps ) => h ( processSlot ( slot ) , props )
145
+ return acc
146
+ }
120
147
121
- if ( typeof slot === 'string' ) {
122
- // slot is most probably a scoped slot string or a plain string
123
- acc [ name ] = ( props ) => h ( processSlot ( slot ) , props )
124
148
return acc
125
- }
126
- } , { } )
149
+ } ,
150
+ { }
151
+ )
127
152
128
153
// override component data with mounting options data
129
154
if ( options ?. data ) {
@@ -142,6 +167,9 @@ export function mount(
142
167
ref : MOUNT_COMPONENT_REF
143
168
} )
144
169
170
+ const global = mergeGlobalProperties ( config . global , options ?. global )
171
+ component . components = { ...component . components , ...global . components }
172
+
145
173
// create the wrapper component
146
174
const Parent = defineComponent ( {
147
175
name : MOUNT_PARENT_NAME ,
@@ -160,14 +188,15 @@ export function mount(
160
188
161
189
// create the app
162
190
const app = createApp ( Parent )
163
- const global = mergeGlobalProperties ( config . global , options ?. global )
164
191
165
192
// global mocks mixin
166
193
if ( global ?. mocks ) {
167
194
const mixin = {
168
195
beforeCreate ( ) {
169
- for ( const [ k , v ] of Object . entries ( global . mocks ) ) {
170
- this [ k ] = v
196
+ for ( const [ k , v ] of Object . entries (
197
+ global . mocks as { [ key : string ] : any }
198
+ ) ) {
199
+ ; ( this as any ) [ k ] = v
171
200
}
172
201
}
173
202
}
@@ -176,34 +205,37 @@ export function mount(
176
205
}
177
206
178
207
// AppConfig
179
- if ( global ?. config ) {
180
- for ( const [ k , v ] of Object . entries ( global . config ) ) {
208
+ if ( global . config ) {
209
+ for ( const [ k , v ] of Object . entries ( global . config ) as [
210
+ keyof Omit < AppConfig , 'isNativeTag' > ,
211
+ any
212
+ ] [ ] ) {
181
213
app . config [ k ] = v
182
214
}
183
215
}
184
216
185
217
// use and plugins from mounting options
186
- if ( global ? .plugins ) {
218
+ if ( global . plugins ) {
187
219
for ( const use of global . plugins ) app . use ( use )
188
220
}
189
221
190
222
// use any mixins from mounting options
191
- if ( global ? .mixins ) {
223
+ if ( global . mixins ) {
192
224
for ( const mixin of global . mixins ) app . mixin ( mixin )
193
225
}
194
226
195
- if ( global ? .components ) {
227
+ if ( global . components ) {
196
228
for ( const key of Object . keys ( global . components ) )
197
229
app . component ( key , global . components [ key ] )
198
230
}
199
231
200
- if ( global ? .directives ) {
232
+ if ( global . directives ) {
201
233
for ( const key of Object . keys ( global . directives ) )
202
234
app . directive ( key , global . directives [ key ] )
203
235
}
204
236
205
237
// provide any values passed via provides mounting option
206
- if ( global ? .provide ) {
238
+ if ( global . provide ) {
207
239
for ( const key of Reflect . ownKeys ( global . provide ) ) {
208
240
// @ts -ignore: https://github.com/microsoft/TypeScript/issues/1863
209
241
app . provide ( key , global . provide [ key ] )
@@ -214,8 +246,8 @@ export function mount(
214
246
app . mixin ( attachEmitListener ( ) )
215
247
216
248
// stubs
217
- if ( options ?. global ? .stubs || options ?. shallow ) {
218
- stubComponents ( options ?. global ? .stubs , options ?. shallow )
249
+ if ( global . stubs || options ?. shallow ) {
250
+ stubComponents ( global . stubs , options ?. shallow )
219
251
} else {
220
252
transformVNodeArgs ( )
221
253
}
@@ -227,9 +259,6 @@ export function mount(
227
259
return createWrapper ( app , App , setProps )
228
260
}
229
261
230
- export function shallowMount (
231
- originalComponent ,
232
- options ?: MountingOptions < any >
233
- ) {
234
- return mount ( originalComponent , { ...options , shallow : true } )
262
+ export const shallowMount : typeof mount = ( component : any , options ?: any ) => {
263
+ return mount ( component , { ...options , shallow : true } )
235
264
}
0 commit comments