乐于分享
好东西不私藏

dsh一切皆插件

dsh一切皆插件

dsh出来也有一段时间,个人比较好奇的是它宣传的两个概念 - 时空可组合性一切皆插件。可惜社区一堆推销plugin的,没见几篇深入这两个概念的。带着这两个兴趣点,就想走走插件的制作和HMR流程,探究一二。

插件化设计


第一步打开了官网,并首先了解到了两点:

  1. 1. 插件框架是基于Cordis的。
  2. 2. 所有插件的开发入口最终都汇聚到apply函数和Context参数
export function apply(ctx: Context, config: Config) {  //从context去获取其它插件.}

然后又了解到了插件之间角色有provider和consumer区分,当然也可以同时是二者。作为consumer时,需要用显式声明依赖,如下:

export const inject = ['tools'] //必需依赖const metrics = ctx.get('metrics') //可选依赖

作为provider时,需要合并声明,这样consumer才能从context入口访问到:

declare module '@deepseek-ai/cordis' {  interface Context {    metrics: MetricsService  }}

到这里,就有点一切皆插件的意思了,协作的基本单元都是插件,而且插件间也能通过事件监听和广播的方式实现通信:

ctx.on('event-name', (payload) => {  // Handle the event.})ctx.emit('event-name', payload)

题外:还有个比较有意思的是“能力的三层拆分”这里,一眼熟悉的OOP的依赖倒置(DIP)和接口分离原则。

插件开发


插件开发主体跟传统npm包开发区别不大,声明依赖,暴露入口,输出目录即可:

 {    name: ID + '/client',    entry: { client: 'src/client/index.ts' },    outDir: 'lib',    format: 'cjs',    platform: 'browser',}

重点是要提供dsh声明,这样dsh才能识别它是一个dsh插件.

"dsh": {  "bundle": {    "patch": "./cordis.patch.yml"  }}

相应的在patch配置处声明你的意图和id,名称等信息,用于加载到运行时上下文.

- insert:    - id: ui-wallpaper      name: 'dsh-wallpaper'

插件安装


安装基于profile,基础命令如下:

npx @deepseek-ai/dsh plugin --profile web add <本地路径或git地址>

注:当添加的路径是本地client插件时,插件dst变更就能同步HMR到web端,这也是我了解到的第一个HMR方式。

不重启插件上下线


应该说是一个很常见的需求,目前探索出来的不重启插件上下线的方式是编辑~/.dsh/profiles/web/cordis.patch.ymlnpx @deepseek-ai/dsh plugin --profile web add之后追加insert配置以上线:

- insert:  - id: ui-pet    name: 'dsh-pet'

下线则设置显式配置disabled: true 

- id: ui-pet  disabled: true

注:只对没声明 dsh.bundle.patch也就是手动挂载的插件成立

总结


总体体验下来是插件化的设计harness的定制化自由度很高,但这份自由度的必要性有多高不得而知。没什么idea,最后也就只能想到基于turn start/end的事件做只pet cat,提供点情绪价值了...