Skip to content

Commit

Permalink
feat(manager): jump to service provider
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jun 1, 2024
1 parent d771a32 commit 8394b55
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 92 deletions.
27 changes: 0 additions & 27 deletions plugins/manager/client/components/group.vue

This file was deleted.

4 changes: 1 addition & 3 deletions plugins/manager/client/components/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@
<div>请在左侧选择插件</div>
</k-empty>
<k-content v-else class="plugin-view" :key="path">
<group-settings v-if="current.isGroup" v-model="config"></group-settings>
<plugin-settings v-else v-model="config"></plugin-settings>
<plugin-settings v-model="config"></plugin-settings>
</k-content>

<el-dialog
Expand Down Expand Up @@ -79,7 +78,6 @@
import { computed, ref, watch, nextTick } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { clone, message, send, useContext, Schema } from '@cordisjs/client'
import GroupSettings from './group.vue'
import TreeView from './tree.vue'
import PluginSettings from './plugin.vue'
import { EntryData } from '../../src'
Expand Down
28 changes: 14 additions & 14 deletions plugins/manager/client/components/plugin.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,14 @@
<k-slot-item :order="800">
<k-slot name="plugin-dependency" single>
<k-comment
v-for="({ required, active }, name) in env.peer" :key="name"
:type="active ? 'success' : required ? 'warning' : 'primary'">
v-for="({ required, provider }, name) in env.using" :key="name"
:type="provider ? 'success' : required ? 'warning' : 'primary'">
<p>
{{ required ? '必需' : '可选' }}依赖:{{ name }} ({{ active ? '已加载' : '未加载' }})
</p>
</k-comment>
<k-comment
v-for="({ required }, name) in env.using" :key="name"
:type="name in data.services ? 'success' : required ? 'warning' : 'primary'">
<p>
{{ required ? '必需' : '可选' }}服务:{{ name }} ({{ name in data.services ? '已加载' : '未加载' }})
{{ required ? '必需' : '可选' }}服务:{{ name }}
<template v-if="provider">
(<span :class="{ 'k-link': provider.length }" @click="gotoProvider(provider)">已加载</span>)
</template>
<template v-else>(未加载)</template>
</p>
</k-comment>
</k-slot>
Expand Down Expand Up @@ -91,7 +88,7 @@

<script lang="ts" setup>
import { send, useContext, useRpc } from '@cordisjs/client'
import { send, router, useContext, useRpc } from '@cordisjs/client'
import { computed, provide, watch } from 'vue'
import { Data } from '../../src'
Expand All @@ -117,7 +114,7 @@ const config = computed({
set: value => emit('update:modelValue', value),
})
const env = computed(() => ctx.manager.getEnvInfo(current.value?.name)!)
const env = computed(() => ctx.manager.getEnvInfo(current.value)!)
const local = computed(() => data.value.packages[current.value?.name!])
const hint = computed(() => local.value.workspace ? '请检查插件源代码。' : '请联系插件作者并反馈此问题。')
Expand All @@ -126,12 +123,15 @@ watch(local, (value) => {
send('manager.package.runtime', { name: value.package.name })
}, { immediate: true })
provide('plugin:name', name)
provide('plugin:env', env)
provide('manager.settings.local', local)
provide('manager.settings.config', config)
provide('manager.settings.current', current)
function gotoProvider(provider: string[]) {
if (!provider.length) return
router.push('/plugins/' + provider[0])
}
</script>

<style lang="scss">
Expand Down
2 changes: 1 addition & 1 deletion plugins/manager/client/components/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ScopeStatus } from '@cordisjs/client'

export function getStatusClass(status: ScopeStatus) {
export function getStatusClass(status?: ScopeStatus) {
switch (status) {
case ScopeStatus.PENDING: return 'pending'
case ScopeStatus.LOADING: return 'loading'
Expand Down
64 changes: 26 additions & 38 deletions plugins/manager/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,12 @@ export interface Node extends EntryData {

interface DepInfo {
required: boolean
}

interface PeerInfo {
required: boolean
active: boolean
provider?: string[]
}

export interface EnvInfo {
impl: string[]
using: Dict<DepInfo>
peer: Dict<PeerInfo>
warning?: boolean
}

Expand Down Expand Up @@ -77,7 +72,7 @@ export default class Manager extends Service {
})

type = computed(() => {
const env = this.getEnvInfo(this.current.value?.name)
const env = this.getEnvInfo(this.current.value)
if (!env) return
if (env.warning && this.current.value!.disabled) return 'warning'
for (const name in env.using) {
Expand All @@ -89,7 +84,7 @@ export default class Manager extends Service {
}
})

constructor(ctx: Context) {
constructor(ctx: Context, public data: Ref<Data>) {
super(ctx, 'manager', true)
}

Expand Down Expand Up @@ -170,10 +165,6 @@ export default class Manager extends Service {
}])
}

get data(): Ref<Data> {
return this.ctx.$entry!.data
}

ensure(name: string, passive?: boolean) {
const forks = this.plugins.value.forks[name]
if (!forks?.length) {
Expand Down Expand Up @@ -204,40 +195,37 @@ export default class Manager extends Service {
return this.plugins.value.forks[name]?.map(id => this.plugins.value.entries[id])
}

getEnvInfo(name?: string) {
function setService(name: string, required: boolean) {
if (services.has(name)) return
if (name === 'console') return
result.using[name] = { required }
}

if (!name) return
const local = this.data.value.packages[name]
if (!local) return
getEnvInfo(entry?: EntryData) {
if (!entry) return
const local = this.data.value.packages[entry.name]
if (!local?.runtime) return

const result: EnvInfo = { impl: [], using: {}, peer: {} }
const services = new Set<string>()

// check peer dependencies
for (const name in local.package.peerDependencies ?? {}) {
// FIXME
if (!name.includes('@cordisjs/plugin-') && !name.includes('cordis-plugin-')) continue
if (coreDeps.includes(name)) continue
const required = !local.package.peerDependenciesMeta?.[name]?.optional
const active = !!this.data.value.packages[name]?.runtime?.id
result.peer[name] = { required, active }
for (const service of this.data.value.packages[name]?.manifest?.service?.implements ?? []) {
services.add(service)
}
}
const result: EnvInfo = { impl: [], using: {} }

// check implementations
for (const name of local.manifest.service?.implements || []) {
result.impl.push(name)
}

// check services
for (const name of local.runtime?.required ?? []) {
const setService = (name: string, required: boolean) => {
let provider = this.data.value.services[name]?.root
let node = entry
while (node) {
const label = node.isolate?.[name]
if (label === true) {
provider = this.data.value.services[name]?.local[node.id]
} else if (label) {
provider = this.data.value.services[name]?.global[node.id]
} else if (node.parent) {
node = this.plugins.value.entries[node.parent]
continue
}
break
}
result.using[name] = { required, provider }
}
for (const name of local.runtime.required ?? []) {
setService(name, true)
}
for (const name of local.runtime?.optional ?? []) {
Expand Down
43 changes: 34 additions & 9 deletions plugins/manager/src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ declare module '@cordisjs/plugin-webui' {
'manager.config.remove'(options: { id: string }): void
'manager.package.list'(): Promise<LocalObject[]>
'manager.package.runtime'(options: { name: string }): Promise<RuntimeData>
'manager.service.list'(): Dict<string[]>
'manager.service.list'(): Promise<Dict<RealmData>>
}
}

Expand All @@ -35,10 +35,16 @@ export interface EntryData extends EntryOptions, Required<EntryLocation> {
status?: ScopeStatus
}

export interface RealmData {
root?: string[]
local: Dict<string[]>
global: Dict<string[]>
}

export interface Data {
entries: EntryData[]
packages: Dict<LocalObject>
services: Dict<string[]>
services: Dict<RealmData>
}

export interface RuntimeData {
Expand Down Expand Up @@ -83,15 +89,34 @@ export abstract class Manager extends Service {
}))
}

locateService(ctx: Context, name: string) {
const instance = ctx.get(name)
if (!(instance instanceof Object)) return
const origin: Context = Reflect.getOwnPropertyDescriptor(instance, Context.current)?.value
if (!origin) return
return this.ctx.loader.locate(origin)
}

getServices() {
const result = {} as Dict<string[]>
const result = {} as Dict<RealmData>
for (const [name, { type }] of Object.entries(this.ctx.root[Context.internal])) {
if (type !== 'service') continue
const instance = this.ctx.get(name)
if (!(instance instanceof Object)) continue
const ctx: Context = Reflect.getOwnPropertyDescriptor(instance, Context.current)?.value
if (!ctx) continue
result[name] = this.ctx.loader.locate(ctx)
result[name] = {
root: this.locateService(this.ctx.root, name),
local: {},
global: {},
}
}
for (const entry of this.ctx.loader.entries()) {
if (!entry.options.isolate) continue
for (const [name, value] of Object.entries(entry.options.isolate)) {
if (!result[name]) continue
if (value === true) {
result[name].local[entry.id] = this.locateService(entry.ctx, name)!
} else {
result[name].global[value] = this.locateService(entry.ctx, name)!
}
}
}
return result
}
Expand Down Expand Up @@ -160,7 +185,7 @@ export abstract class Manager extends Service {
return runtime
})

ctx.webui.addListener('manager.service.list', () => {
ctx.webui.addListener('manager.service.list', async () => {
return this.getServices()
})
})
Expand Down

0 comments on commit 8394b55

Please sign in to comment.