Skip to content

Commit a2f5ee5

Browse files
committed
feat(api): proxy with queue before plugin api is available
1 parent 9fdeb28 commit a2f5ee5

File tree

8 files changed

+127
-28
lines changed

8 files changed

+127
-28
lines changed

packages/api/src/api/api.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,18 @@ import { ID } from './util'
66

77
export interface DevtoolsPluginApi {
88
on: Hookable<Context>
9-
notifyComponentUpdate (instance?: ComponentInstance)
10-
addTimelineLayer (options: TimelineLayerOptions)
11-
addTimelineEvent (options: TimelineEventOptions)
12-
addInspector (options: CustomInspectorOptions)
13-
sendInspectorTree (inspectorId: string)
14-
sendInspectorState (inspectorId: string)
15-
selectInspectorNode (inspectorId: string, nodeId: string)
9+
notifyComponentUpdate (instance?: ComponentInstance): void
10+
addTimelineLayer (options: TimelineLayerOptions): void
11+
addTimelineEvent (options: TimelineEventOptions): void
12+
addInspector (options: CustomInspectorOptions): void
13+
sendInspectorTree (inspectorId: string): void
14+
sendInspectorState (inspectorId: string): void
15+
selectInspectorNode (inspectorId: string, nodeId: string): void
1616
getComponentBounds (instance: ComponentInstance): Promise<ComponentBounds>
1717
getComponentName (instance: ComponentInstance): Promise<string>
1818
getComponentInstances (app: App): Promise<ComponentInstance[]>
19-
highlightElement (instance: ComponentInstance)
20-
unhighlightElement ()
19+
highlightElement (instance: ComponentInstance): void
20+
unhighlightElement (): void
2121
}
2222

2323
export interface AppRecord {

packages/api/src/env.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import { PluginDescriptor, SetupFunction } from '.'
2-
import { HookHandler } from './api'
2+
import { ApiProxy } from './proxy'
3+
4+
export interface PluginQueueItem {
5+
pluginDescriptor: PluginDescriptor
6+
setupFn: SetupFunction
7+
proxy?: ApiProxy
8+
}
39

410
interface GlobalTarget {
5-
__VUE_DEVTOOLS_PLUGINS__: Array<{
6-
pluginDescriptor: PluginDescriptor
7-
setupFn: SetupFunction
8-
}>
11+
__VUE_DEVTOOLS_PLUGINS__?: PluginQueueItem[]
12+
__VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__?: boolean
913
}
1014

1115
export function getDevtoolsGlobalHook (): any {
@@ -20,3 +24,5 @@ export function getTarget (): GlobalTarget {
2024
? global
2125
: {}
2226
}
27+
28+
export const isProxyAvailable = typeof Proxy === 'function'

packages/api/src/index.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { getTarget, getDevtoolsGlobalHook } from './env'
1+
import { getTarget, getDevtoolsGlobalHook, isProxyAvailable } from './env'
22
import { HOOK_SETUP } from './const'
33
import { DevtoolsPluginApi, App } from './api'
4+
import { ApiProxy } from './proxy'
45

56
export * from './api'
7+
export { PluginQueueItem } from './env'
68

79
export interface PluginDescriptor {
810
id: string
@@ -18,15 +20,20 @@ export interface PluginDescriptor {
1820
export type SetupFunction = (api: DevtoolsPluginApi) => void
1921

2022
export function setupDevtoolsPlugin (pluginDescriptor: PluginDescriptor, setupFn: SetupFunction) {
23+
const target = getTarget()
2124
const hook = getDevtoolsGlobalHook()
22-
if (hook) {
25+
if (hook && (target.__VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__ || !isProxyAvailable)) {
2326
hook.emit(HOOK_SETUP, pluginDescriptor, setupFn)
2427
} else {
25-
const target = getTarget()
28+
const proxy = isProxyAvailable ? new ApiProxy() : null
29+
2630
const list = target.__VUE_DEVTOOLS_PLUGINS__ = target.__VUE_DEVTOOLS_PLUGINS__ || []
2731
list.push({
2832
pluginDescriptor,
29-
setupFn
33+
setupFn,
34+
proxy
3035
})
36+
37+
setupFn(proxy.proxiedTarget as DevtoolsPluginApi)
3138
}
3239
}

packages/api/src/proxy.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { Context, DevtoolsPluginApi, Hookable } from './api'
2+
3+
interface QueueItem {
4+
method: string
5+
args: any[]
6+
resolve?: (value?: any) => void
7+
}
8+
9+
export class ApiProxy<TTarget extends DevtoolsPluginApi = DevtoolsPluginApi> {
10+
target: TTarget | null
11+
targetQueue: QueueItem[]
12+
proxiedTarget: TTarget
13+
14+
onQueue: QueueItem[]
15+
proxiedOn: Hookable<Context>
16+
17+
constructor () {
18+
this.target = null
19+
this.targetQueue = []
20+
this.onQueue = []
21+
22+
this.proxiedOn = new Proxy({} as Hookable<Context>, {
23+
get: (_target, prop: string) => {
24+
if (this.target) {
25+
return this.target.on[prop]
26+
} else {
27+
return (...args) => {
28+
this.onQueue.push({
29+
method: prop,
30+
args
31+
})
32+
}
33+
}
34+
}
35+
})
36+
37+
this.proxiedTarget = new Proxy({} as TTarget, {
38+
get: (_target, prop: string) => {
39+
if (this.target) {
40+
return this.target[prop]
41+
} else if (prop === 'on') {
42+
return this.proxiedOn
43+
} else {
44+
return (...args) => {
45+
return new Promise(resolve => {
46+
this.targetQueue.push({
47+
method: prop,
48+
args,
49+
resolve
50+
})
51+
})
52+
}
53+
}
54+
}
55+
})
56+
}
57+
58+
async setRealTarget (target: TTarget) {
59+
this.target = target
60+
61+
for (const item of this.onQueue) {
62+
this.target.on[item.method](...item.args)
63+
}
64+
65+
for (const item of this.targetQueue) {
66+
item.resolve(await this.target[item.method](...item.args))
67+
}
68+
}
69+
}

packages/app-backend-core/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,9 +286,11 @@ async function connect () {
286286
addQueuedPlugins(ctx)
287287

288288
hook.on(HookEvents.SETUP_DEVTOOLS_PLUGIN, async (pluginDescriptor: PluginDescriptor, setupFn: SetupFunction) => {
289-
await addPlugin(pluginDescriptor, setupFn, ctx)
289+
await addPlugin({ pluginDescriptor, setupFn }, ctx)
290290
})
291291

292+
target.__VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__ = true
293+
292294
// Legacy flush
293295
hook.off('flush')
294296
hook.on('flush', () => {

packages/app-backend-core/src/plugin.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import { PluginDescriptor, SetupFunction } from '@vue/devtools-api'
1+
import { PluginQueueItem } from '@vue/devtools-api'
22
import { Plugin, BackendContext, DevtoolsPluginApiInstance } from '@vue-devtools/app-backend-api'
33
import { BridgeEvents, target } from '@vue-devtools/shared-utils'
44
import { getAppRecord, getAppRecordId } from './app'
55

6-
export async function addPlugin (pluginDescriptor: PluginDescriptor, setupFn: SetupFunction, ctx: BackendContext) {
6+
export async function addPlugin (pluginQueueItem: PluginQueueItem, ctx: BackendContext) {
7+
const { pluginDescriptor, setupFn } = pluginQueueItem
8+
79
const plugin: Plugin = {
810
descriptor: pluginDescriptor,
911
setupFn,
@@ -13,7 +15,11 @@ export async function addPlugin (pluginDescriptor: PluginDescriptor, setupFn: Se
1315
try {
1416
await getAppRecord(plugin.descriptor.app, ctx)
1517
const api = new DevtoolsPluginApiInstance(plugin, ctx)
16-
setupFn(api)
18+
if (pluginQueueItem.proxy) {
19+
await pluginQueueItem.proxy.setRealTarget(api)
20+
} else {
21+
setupFn(api)
22+
}
1723
} catch (e) {
1824
plugin.error = e
1925
console.error(e)
@@ -33,17 +39,17 @@ export async function addPlugin (pluginDescriptor: PluginDescriptor, setupFn: Se
3339

3440
export async function addQueuedPlugins (ctx: BackendContext) {
3541
if (target.__VUE_DEVTOOLS_PLUGINS__ && Array.isArray(target.__VUE_DEVTOOLS_PLUGINS__)) {
36-
for (const plugin of target.__VUE_DEVTOOLS_PLUGINS__) {
37-
await addPlugin(plugin.pluginDescriptor, plugin.setupFn, ctx)
42+
for (const queueItem of target.__VUE_DEVTOOLS_PLUGINS__) {
43+
await addPlugin(queueItem, ctx)
3844
}
3945
target.__VUE_DEVTOOLS_PLUGINS__ = null
4046
}
4147
}
4248

4349
export async function addPreviouslyRegisteredPlugins (ctx: BackendContext) {
4450
if (target.__VUE_DEVTOOLS_REGISTERED_PLUGINS__ && Array.isArray(target.__VUE_DEVTOOLS_REGISTERED_PLUGINS__)) {
45-
for (const plugin of target.__VUE_DEVTOOLS_REGISTERED_PLUGINS__) {
46-
await addPlugin(plugin.pluginDescriptor, plugin.setupFn, ctx)
51+
for (const queueItem of target.__VUE_DEVTOOLS_REGISTERED_PLUGINS__) {
52+
await addPlugin(queueItem, ctx)
4753
}
4854
}
4955
}

packages/shell-dev-vue3/src/App.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ export default {
109109
<h2>Store</h2>
110110
<div>
111111
{{ $store.getters.answer }}
112+
<button @click="$store.commit('increment')">
113+
+1
114+
</button>
112115
{{ $store.getters.twoFoo }}
113116
</div>
114117

packages/shell-dev-vue3/src/store.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@ import { createStore } from 'vuex'
33
const store = createStore({
44
state () {
55
return {
6-
rootState: 'root'
6+
rootState: 'root',
7+
answer: 42
78
}
89
},
910
getters: {
10-
answer: () => 42
11+
answer: (state) => state.answer
12+
},
13+
mutations: {
14+
increment (state) {
15+
state.answer++
16+
}
1117
},
1218
modules: {
1319
nested: {

0 commit comments

Comments
 (0)