|
1 |
| -import type { VueConstructor } from 'vue' |
2 |
| -import { ComponentInstance } from './component' |
3 |
| -import { assert, hasOwn, warn } from './utils' |
| 1 | +import type { VueConstructor, VNode } from 'vue' |
| 2 | +import { ComponentInstance, Data } from './component' |
| 3 | +import { assert, hasOwn, warn, proxy, UnionToIntersection } from './utils' |
4 | 4 |
|
5 | 5 | let vueDependency: VueConstructor | undefined = undefined
|
6 | 6 |
|
@@ -71,10 +71,168 @@ export function setVueConstructor(Vue: VueConstructor) {
|
71 | 71 | })
|
72 | 72 | }
|
73 | 73 |
|
74 |
| -export function getCurrentInstance(): ComponentInstance | null { |
| 74 | +export function setCurrentInstance(vm: ComponentInstance | null) { |
| 75 | + // currentInstance?.$scopedSlots |
| 76 | + currentInstance = vm |
| 77 | +} |
| 78 | + |
| 79 | +export type Slot = (...args: any[]) => VNode[] |
| 80 | + |
| 81 | +export type InternalSlots = { |
| 82 | + [name: string]: Slot | undefined |
| 83 | +} |
| 84 | + |
| 85 | +export type ObjectEmitsOptions = Record< |
| 86 | + string, |
| 87 | + ((...args: any[]) => any) | null |
| 88 | +> |
| 89 | +export type EmitsOptions = ObjectEmitsOptions | string[] |
| 90 | + |
| 91 | +export type EmitFn< |
| 92 | + Options = ObjectEmitsOptions, |
| 93 | + Event extends keyof Options = keyof Options |
| 94 | +> = Options extends Array<infer V> |
| 95 | + ? (event: V, ...args: any[]) => void |
| 96 | + : {} extends Options // if the emit is empty object (usually the default value for emit) should be converted to function |
| 97 | + ? (event: string, ...args: any[]) => void |
| 98 | + : UnionToIntersection< |
| 99 | + { |
| 100 | + [key in Event]: Options[key] extends (...args: infer Args) => any |
| 101 | + ? (event: key, ...args: Args) => void |
| 102 | + : (event: key, ...args: any[]) => void |
| 103 | + }[Event] |
| 104 | + > |
| 105 | + |
| 106 | +/** |
| 107 | + * We expose a subset of properties on the internal instance as they are |
| 108 | + * useful for advanced external libraries and tools. |
| 109 | + */ |
| 110 | +export declare interface ComponentInternalInstance { |
| 111 | + uid: number |
| 112 | + // type: ConcreteComponent |
| 113 | + parent: ComponentInternalInstance | null |
| 114 | + root: ComponentInternalInstance |
| 115 | + |
| 116 | + //appContext: AppContext |
| 117 | + |
| 118 | + /** |
| 119 | + * Vnode representing this component in its parent's vdom tree |
| 120 | + */ |
| 121 | + vnode: VNode |
| 122 | + /** |
| 123 | + * Root vnode of this component's own vdom tree |
| 124 | + */ |
| 125 | + // subTree: VNode // does not exist in Vue 2 |
| 126 | + |
| 127 | + /** |
| 128 | + * The reactive effect for rendering and patching the component. Callable. |
| 129 | + */ |
| 130 | + update: Function |
| 131 | + |
| 132 | + data: Data |
| 133 | + props: Data |
| 134 | + attrs: Data |
| 135 | + refs: Data |
| 136 | + emit: EmitFn |
| 137 | + |
| 138 | + slots: InternalSlots |
| 139 | + emitted: Record<string, boolean> | null |
| 140 | + |
| 141 | + proxy: ComponentInstance |
| 142 | + |
| 143 | + isMounted: boolean |
| 144 | + isUnmounted: boolean |
| 145 | + isDeactivated: boolean |
| 146 | +} |
| 147 | + |
| 148 | +export function getCurrentVu2Instance() { |
75 | 149 | return currentInstance
|
76 | 150 | }
|
77 | 151 |
|
78 |
| -export function setCurrentInstance(vm: ComponentInstance | null) { |
79 |
| - currentInstance = vm |
| 152 | +export function getCurrentInstance() { |
| 153 | + if (currentInstance) { |
| 154 | + return toVue3ComponentInstance(currentInstance) |
| 155 | + } |
| 156 | + return null |
| 157 | +} |
| 158 | + |
| 159 | +const instanceMapCache = new WeakMap< |
| 160 | + ComponentInstance, |
| 161 | + ComponentInternalInstance |
| 162 | +>() |
| 163 | + |
| 164 | +function toVue3ComponentInstance( |
| 165 | + vue2Instance: ComponentInstance |
| 166 | +): ComponentInternalInstance { |
| 167 | + if (instanceMapCache.has(vue2Instance)) { |
| 168 | + return instanceMapCache.get(vue2Instance)! |
| 169 | + } |
| 170 | + |
| 171 | + const instance: ComponentInternalInstance = ({ |
| 172 | + proxy: vue2Instance, |
| 173 | + update: vue2Instance.$forceUpdate, |
| 174 | + uid: vue2Instance._uid, |
| 175 | + |
| 176 | + parent: null, |
| 177 | + root: null as any, |
| 178 | + } as unknown) as ComponentInternalInstance |
| 179 | + |
| 180 | + // map vm.$props = |
| 181 | + const instanceProps = [ |
| 182 | + 'data', |
| 183 | + 'props', |
| 184 | + 'attrs', |
| 185 | + 'refs', |
| 186 | + 'emit', |
| 187 | + 'vnode', |
| 188 | + 'slots', |
| 189 | + ] as const |
| 190 | + |
| 191 | + instanceProps.forEach((prop) => { |
| 192 | + proxy(instance, prop, { |
| 193 | + get() { |
| 194 | + return (vue2Instance as any)[`$${prop}`] |
| 195 | + }, |
| 196 | + }) |
| 197 | + }) |
| 198 | + |
| 199 | + proxy(instance, 'isMounted', { |
| 200 | + get() { |
| 201 | + // @ts-expect-error private api |
| 202 | + return vue2Instance._isMounted |
| 203 | + }, |
| 204 | + }) |
| 205 | + |
| 206 | + proxy(instance, 'isUnmounted', { |
| 207 | + get() { |
| 208 | + // @ts-expect-error private api |
| 209 | + return vue2Instance._isDestroyed |
| 210 | + }, |
| 211 | + }) |
| 212 | + |
| 213 | + proxy(instance, 'isDeactivated', { |
| 214 | + get() { |
| 215 | + // @ts-expect-error private api |
| 216 | + return vue2Instance._inactive |
| 217 | + }, |
| 218 | + }) |
| 219 | + |
| 220 | + proxy(instance, 'emitted', { |
| 221 | + get() { |
| 222 | + // @ts-expect-error private api |
| 223 | + return vue2Instance._events |
| 224 | + }, |
| 225 | + }) |
| 226 | + |
| 227 | + instanceMapCache.set(vue2Instance, instance) |
| 228 | + |
| 229 | + if (vue2Instance.$parent) { |
| 230 | + instance.parent = toVue3ComponentInstance(vue2Instance.$parent) |
| 231 | + } |
| 232 | + |
| 233 | + if (vue2Instance.$root) { |
| 234 | + instance.root = toVue3ComponentInstance(vue2Instance.$root) |
| 235 | + } |
| 236 | + |
| 237 | + return instance |
80 | 238 | }
|
0 commit comments