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 type Context:运行时不依赖具体包,编译时有类型
  • 插件三种形态:函数(本例,最常见)/ 对象(带 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' },
})

注意两点:

  1. Loader 本身也是插件(ctx.plugin(Loader))——体现「无特权核心」:连加载器、logger、定时器都和业务插件地位平等
  2. 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)决定,而非文件中的位置(下一课详解)。

动手练习

  1. 复制 lessons/01-first-plugin/ 为新目录,改 hello.ts 的文案和 name,改 cordis.yml 指向它,用 node --import tsx bin.js lessons/<新目录>/cordis.yml 运行
  2. 在 yml 里写两个相同插件条目,观察 apply 被调用两次(每个条目 = 一个插件实例)
  3. 删掉 export const name 再运行——能跑,但诊断信息里只能看到路径(后面第 03 章诊断会用到它)

下一课:02-lifecycle-effect