01 · 第一个插件:环境搭建与最小示例
目标:跑通第一个插件,理解「插件 = 函数」「应用 = cordis.yml 数据」两个核心等式。 对应快速入门项目:
npm run lesson:01(~/tmp/proj/cordis),总览见 README
实例:Hello Cordis
Step 1:写插件
// hello.ts —— 插件就是一个函数
import type { Context } from 'cordis'
// name 是可选的显示元数据,用于诊断信息中标识插件
export const name = 'hello'
export function apply(ctx: Context) {
console.log('hello from my first cordis plugin')
}要点:
apply(ctx)在插件加载时被调用,ctx是上下文——插件通过它注册一切贡献- 只
import typeContext:运行时不依赖具体包,编译时有类型 - 插件三种形态:函数(本例,最常见)/ 对象(带
apply方法)/ 类(Service子类,下一课见)
Step 2:写组合文件
# cordis.yml —— 应用 = 一份插件组合数据
- name: './hello.ts'name 是模块指定符:可以是相对路径(基于本 yml 所在目录解析)或 npm 包名。这就是「组合方式是数据」的含义——应用不由代码硬编码,而由这份清单决定。
Step 3:运行
npm run lesson:01预期输出:
hello from my first cordis plugin
启动器原理:bin.js 做了什么
import { Context } from 'cordis'
import { pathToFileURL } from 'node:url'
import Loader from '@cordisjs/plugin-loader'
const ctx = new Context() // 1. 根 Context
ctx.baseUrl = pathToFileURL(process.cwd()).href + '/'
await ctx.plugin(Loader) // 2. 挂 Loader 插件
await ctx.loader.create({ // 3. 读指定 yml
name: '@cordisjs/plugin-include',
config: { path: process.argv[2] ?? './cordis.yml' },
})注意两点:
- Loader 本身也是插件(
ctx.plugin(Loader))——体现「无特权核心」:连加载器、logger、定时器都和业务插件地位平等 - Include 会把 baseUrl 重置为 yml 所在目录:yml 里的
'./xxx.ts'相对 yml 自己解析,与你在哪个目录运行无关
运行命令里的 --import tsx 让 Node 直接执行 .ts(配置文件和插件都能用 TS 写):
node --import tsx bin.js lessons/01-first-plugin/cordis.yml多个插件 = 多行条目
聚合演示(npm start)就是一份组合了 10 个条目的 cordis.yml:
- name: './lessons/01-first-plugin/hello.ts'
- name: './lessons/03-services/greeter.ts'
- name: './lessons/03-services/consumer.ts'
# ...各条目并发启动:加载先后由服务依赖(inject)决定,而非文件中的位置(下一课详解)。
动手练习
- 复制
lessons/01-first-plugin/为新目录,改hello.ts的文案和name,改cordis.yml指向它,用node --import tsx bin.js lessons/<新目录>/cordis.yml运行 - 在 yml 里写两个相同插件条目,观察
apply被调用两次(每个条目 = 一个插件实例) - 删掉
export const name再运行——能跑,但诊断信息里只能看到路径(后面第 03 章诊断会用到它)