@zhin.js/dependency 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +769 -0
- package/dist/dependency.d.ts +42 -0
- package/dist/dependency.d.ts.map +1 -0
- package/dist/dependency.js +295 -0
- package/dist/dependency.js.map +1 -0
- package/dist/hook-registry.d.ts +37 -0
- package/dist/hook-registry.d.ts.map +1 -0
- package/dist/hook-registry.js +75 -0
- package/dist/hook-registry.js.map +1 -0
- package/dist/hooks.d.ts +6 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +44 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/loaders/bun-plugin.ts +86 -0
- package/loaders/bun-preload.ts +16 -0
- package/loaders/register.mjs +18 -0
- package/loaders/transform-utils.mjs +108 -0
- package/loaders/tsx-loader.mjs +68 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,769 @@
|
|
|
1
|
+
# 🌲 @zhin.js/dependency
|
|
2
|
+
|
|
3
|
+
一个强大的依赖树分析工具,支持动态导入、热重载、生命周期管理和可扩展的 Hook 系统。
|
|
4
|
+
|
|
5
|
+
## 📋 目录
|
|
6
|
+
|
|
7
|
+
- [主要特性](#-主要特性)
|
|
8
|
+
- [安装](#-安装)
|
|
9
|
+
- [快速开始](#-快速开始)
|
|
10
|
+
- [基本用法](#基本用法)
|
|
11
|
+
- [在插件中使用 Hooks](#在插件中使用-hooks)
|
|
12
|
+
- [继承 Dependency 类](#继承-dependency-类)
|
|
13
|
+
- [配置](#-配置)
|
|
14
|
+
- [环境变量](#环境变量)
|
|
15
|
+
- [运行时配置](#运行时配置)
|
|
16
|
+
- [可扩展 Hook 系统](#-可扩展-hook-系统)
|
|
17
|
+
- [热重载](#-热重载)
|
|
18
|
+
- [类继承指南](#-类继承指南)
|
|
19
|
+
- [插件生态系统](#-插件生态系统)
|
|
20
|
+
- [API 文档](#-api-文档)
|
|
21
|
+
- [生命周期](#-生命周期)
|
|
22
|
+
|
|
23
|
+
## ✨ 主要特性
|
|
24
|
+
|
|
25
|
+
- 🌲 **依赖树构建** - 自动构建模块依赖关系树
|
|
26
|
+
- 🔄 **热重载支持** - 文件变更时自动重载,保留子依赖树
|
|
27
|
+
- 🎯 **原生 import 支持** - 使用标准 ES 模块语法,无需自定义函数
|
|
28
|
+
- 🪝 **可扩展 Hook 系统** - 注册自定义 hooks,支持自动类型推断
|
|
29
|
+
- 📦 **跨运行时支持** - Node.js / tsx / Bun
|
|
30
|
+
- 🎨 **生命周期管理** - `start`, `mount`, `dispose`, `stop` 生命周期方法
|
|
31
|
+
- 🔔 **EventEmitter 集成** - 标准的事件系统
|
|
32
|
+
- 🎯 **TypeScript 类型支持** - 完整的类型定义和类型推断
|
|
33
|
+
- 🧬 **继承支持** - 完全支持类继承,创建自定义插件类
|
|
34
|
+
|
|
35
|
+
## 📦 安装
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pnpm add @zhin.js/dependency
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 🚀 快速开始
|
|
42
|
+
|
|
43
|
+
### 基本用法
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { Dependency } from '@zhin.js/dependency';
|
|
47
|
+
|
|
48
|
+
// 创建并启动依赖树
|
|
49
|
+
const root = new Dependency('./entry.js');
|
|
50
|
+
await root.start();
|
|
51
|
+
|
|
52
|
+
// 打印依赖树
|
|
53
|
+
console.log(root.printTree('', true, true));
|
|
54
|
+
|
|
55
|
+
// 停止依赖树
|
|
56
|
+
await root.stop();
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 在插件中使用 Hooks
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// plugins/my-plugin.ts
|
|
63
|
+
import { onMount, onDispose, addListener } from '@zhin.js/dependency';
|
|
64
|
+
|
|
65
|
+
// 挂载钩子
|
|
66
|
+
onMount(() => {
|
|
67
|
+
console.log('插件已挂载!');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// 添加事件监听器
|
|
71
|
+
const unsubscribe = addListener('my-event', () => {
|
|
72
|
+
console.log('事件触发');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// 卸载钩子
|
|
76
|
+
onDispose(() => {
|
|
77
|
+
unsubscribe();
|
|
78
|
+
console.log('插件已卸载');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// 使用原生 import 导入子模块
|
|
82
|
+
import './child-plugin';
|
|
83
|
+
|
|
84
|
+
export default {};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 继承 Dependency 类
|
|
88
|
+
|
|
89
|
+
完全支持类继承,创建自定义插件类:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { Dependency } from '@zhin.js/dependency';
|
|
93
|
+
|
|
94
|
+
class Plugin extends Dependency {
|
|
95
|
+
public version: string = '1.0.0';
|
|
96
|
+
|
|
97
|
+
constructor(filePath: string) {
|
|
98
|
+
super(filePath);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
getInfo(): string {
|
|
102
|
+
return `${this.name} v${this.version}`;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 使用自定义类
|
|
107
|
+
const root = new Plugin('./entry.js');
|
|
108
|
+
await root.start();
|
|
109
|
+
|
|
110
|
+
// 所有子节点也是 Plugin 实例!
|
|
111
|
+
console.log(root.children[0] instanceof Plugin); // true
|
|
112
|
+
console.log(root.children[0].getInfo()); // 可以使用自定义方法
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## 🔧 配置
|
|
116
|
+
|
|
117
|
+
### 环境变量
|
|
118
|
+
|
|
119
|
+
#### `DEPENDENCY_TREE_INCLUDE`
|
|
120
|
+
|
|
121
|
+
指定需要处理的路径(优先级最高,即使在 `node_modules` 中也会处理)。
|
|
122
|
+
|
|
123
|
+
**使用场景:**
|
|
124
|
+
|
|
125
|
+
1. **包含 npm 包中的插件** ⭐
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# 场景:你的插件发布为 npm 包,用户安装后需要被依赖树系统处理
|
|
129
|
+
DEPENDENCY_TREE_INCLUDE=node_modules/@my-org/my-plugin
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
2. **混合本地和 npm 插件**
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# 同时包含本地插件和多个 npm 包
|
|
136
|
+
DEPENDENCY_TREE_INCLUDE=src/plugins,node_modules/@org/plugin1,node_modules/@org/plugin2
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
3. **包含包内特定目录**
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# 只处理包内的 plugins 目录
|
|
143
|
+
DEPENDENCY_TREE_INCLUDE=node_modules/@my-org/my-plugin/plugins
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
4. **支持插件生态系统**(社区插件 + 官方插件)⭐
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# 同时支持社区插件 (zhin.js-*) 和官方插件 (@zhin.js/*)
|
|
150
|
+
DEPENDENCY_TREE_INCLUDE=node_modules/zhin.js-,node_modules/@zhin.js/
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
#### `DEPENDENCY_TREE_EXCLUDE`
|
|
154
|
+
|
|
155
|
+
指定需要排除的路径(优先级第二)。
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
# 排除测试文件
|
|
159
|
+
DEPENDENCY_TREE_EXCLUDE=plugins/__tests__,plugins/**/*.test.ts
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 运行时配置
|
|
163
|
+
|
|
164
|
+
#### Bun
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"scripts": {
|
|
169
|
+
"start": "bun --preload @zhin.js/dependency/bun-preload.ts src/index.ts"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
#### tsx
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"scripts": {
|
|
179
|
+
"start": "tsx --import @zhin.js/dependency/register.mjs src/index.ts"
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### Node.js(编译后)
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"scripts": {
|
|
189
|
+
"build": "tsc",
|
|
190
|
+
"start": "node --import @zhin.js/dependency/register.mjs dist/index.js"
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## 🪝 可扩展 Hook 系统
|
|
196
|
+
|
|
197
|
+
### 自动类型推断
|
|
198
|
+
|
|
199
|
+
通过 **Module Augmentation** 扩展 `Hooks` interface,实现自动类型推断:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import { registerHook, useHook } from '@zhin.js/dependency';
|
|
203
|
+
|
|
204
|
+
// 1️⃣ 扩展类型定义
|
|
205
|
+
declare module '@zhin.js/dependency' {
|
|
206
|
+
interface Hooks {
|
|
207
|
+
logger: (message: string, level?: 'info' | 'warn' | 'error') => void;
|
|
208
|
+
onBeforeMount: (callback: () => void) => void;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// 2️⃣ 注册 hook
|
|
213
|
+
registerHook({
|
|
214
|
+
name: 'logger',
|
|
215
|
+
handler: (dep, message, level = 'info') => {
|
|
216
|
+
console[level](`[${dep.name}] ${message}`);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// 3️⃣ 使用(类型自动推断!)
|
|
221
|
+
export const logger = useHook('logger'); // (message: string, level?: 'info' | 'warn' | 'error') => void
|
|
222
|
+
|
|
223
|
+
// ✅ TypeScript 提供完整的类型检查和智能提示
|
|
224
|
+
logger('Hello', 'info');
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### 内置 Hooks
|
|
228
|
+
|
|
229
|
+
- `addListener(event, listener)` - 添加事件监听器
|
|
230
|
+
- `onMount(hook)` - 添加挂载钩子
|
|
231
|
+
- `onDispose(hook)` - 添加卸载钩子
|
|
232
|
+
- `importModule(path)` - 导入子模块
|
|
233
|
+
|
|
234
|
+
### 自定义 Hooks API
|
|
235
|
+
|
|
236
|
+
- `registerHook(config)` - 注册自定义 hook
|
|
237
|
+
- `unregisterHook(name)` - 取消注册 hook
|
|
238
|
+
- `useHook(name)` - 创建 hook 函数(支持类型推断)
|
|
239
|
+
- `hasHook(name)` - 检查 hook 是否存在
|
|
240
|
+
- `getAllHooks()` - 获取所有已注册 hooks
|
|
241
|
+
|
|
242
|
+
## 🔥 热重载
|
|
243
|
+
|
|
244
|
+
`Dependency` 提供了 `reload()` 方法来支持热重载。你可以使用 `chokidar` 监听文件变化,然后调用 `reload()` 来重新加载模块。
|
|
245
|
+
|
|
246
|
+
### 基本示例
|
|
247
|
+
|
|
248
|
+
使用**事件驱动**的方式动态收集文件路径:
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
import { Dependency } from '@zhin.js/dependency';
|
|
252
|
+
import chokidar from 'chokidar';
|
|
253
|
+
|
|
254
|
+
// 1. 创建依赖树和文件监听器
|
|
255
|
+
const root = new Dependency('./entry.js');
|
|
256
|
+
const watchedFiles = new Map<string, Dependency>();
|
|
257
|
+
|
|
258
|
+
// 2. 创建空的 watcher,准备动态添加文件
|
|
259
|
+
const watcher = chokidar.watch([], {
|
|
260
|
+
persistent: true,
|
|
261
|
+
ignoreInitial: true,
|
|
262
|
+
awaitWriteFinish: {
|
|
263
|
+
stabilityThreshold: 100,
|
|
264
|
+
pollInterval: 100
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// 3. 监听 afterStart 事件,动态收集文件路径
|
|
269
|
+
root.on('afterStart', (dep: Dependency) => {
|
|
270
|
+
watchedFiles.set(dep.filePath, dep);
|
|
271
|
+
watcher.add(dep.filePath);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// 4. 启动依赖树(会触发 afterStart 事件)
|
|
275
|
+
await root.start();
|
|
276
|
+
|
|
277
|
+
// 5. 监听文件变化
|
|
278
|
+
watcher.on('change', async (changedPath: string) => {
|
|
279
|
+
const dep = watchedFiles.get(changedPath);
|
|
280
|
+
if (dep) {
|
|
281
|
+
console.log(`📝 文件变更: ${dep.name}`);
|
|
282
|
+
console.time('reload');
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
const newDep = await dep.reload();
|
|
286
|
+
watchedFiles.set(newDep.filePath, newDep);
|
|
287
|
+
} catch (error) {
|
|
288
|
+
console.error(`❌ [${dep.name}] 重载失败:`, error);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
console.timeEnd('reload');
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### 热重载工作原理
|
|
297
|
+
|
|
298
|
+
当调用 `dep.reload()` 时,会自动:
|
|
299
|
+
|
|
300
|
+
1. **暂存子依赖** - 保存当前节点的 children
|
|
301
|
+
2. **卸载当前节点** - 调用 `dispose()`
|
|
302
|
+
3. **清除模块缓存** - 清除 require/import 缓存
|
|
303
|
+
4. **重新导入** - 父节点重新 import 该文件(或根节点重新 start)
|
|
304
|
+
5. **恢复子依赖** - 将暂存的 children 赋值给新节点
|
|
305
|
+
6. **重新挂载** - 调用新节点的 `mount()`
|
|
306
|
+
7. **返回新实例** - `reload()` 返回新的 `Dependency` 实例
|
|
307
|
+
|
|
308
|
+
### 关键特性
|
|
309
|
+
|
|
310
|
+
- ✅ **支持根节点热重载** - 即使没有 parent 也能 reload
|
|
311
|
+
- ✅ **返回新实例** - `reload()` 返回 `Promise<Dependency>`
|
|
312
|
+
- ✅ **事件驱动** - 使用 `afterStart` 事件动态收集依赖
|
|
313
|
+
- ✅ **保留子树** - 子依赖会自动迁移到新实例
|
|
314
|
+
- ✅ **灵活可控** - 完全控制监听策略和重载时机
|
|
315
|
+
|
|
316
|
+
### 优势
|
|
317
|
+
|
|
318
|
+
- 🚀 **性能优化** - 只监听实际需要的文件
|
|
319
|
+
- 🎯 **精确控制** - 可以根据需求定制监听策略
|
|
320
|
+
- 🔄 **增量更新** - 无需重新收集所有文件
|
|
321
|
+
- 💾 **内存友好** - 及时更新监听映射,避免内存泄漏
|
|
322
|
+
- 🛠️ **可扩展** - 可以结合其他工具(如 nodemon、pm2)
|
|
323
|
+
|
|
324
|
+
## 🧬 类继承指南
|
|
325
|
+
|
|
326
|
+
### 核心特性
|
|
327
|
+
|
|
328
|
+
- ✅ **完整继承支持** - 子节点自动使用父节点的类
|
|
329
|
+
- ✅ **类型安全** - 完整的 TypeScript 类型支持
|
|
330
|
+
- ✅ **生命周期保留** - 所有生命周期方法正常工作
|
|
331
|
+
- ✅ **热重载兼容** - 重载后的节点保持相同类型
|
|
332
|
+
|
|
333
|
+
### 基本继承
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
import { Dependency } from '@zhin.js/dependency';
|
|
337
|
+
|
|
338
|
+
class Plugin extends Dependency {
|
|
339
|
+
public version: string = '1.0.0';
|
|
340
|
+
public author: string = 'unknown';
|
|
341
|
+
|
|
342
|
+
constructor(filePath: string) {
|
|
343
|
+
super(filePath);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// 添加自定义方法
|
|
347
|
+
getInfo(): string {
|
|
348
|
+
return `${this.name} v${this.version} by ${this.author}`;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// 使用自定义类
|
|
353
|
+
const root = new Plugin('./entry.js');
|
|
354
|
+
await root.start();
|
|
355
|
+
|
|
356
|
+
// 所有子节点也是 Plugin 实例!
|
|
357
|
+
console.log(root.children[0] instanceof Plugin); // true
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### 实际示例
|
|
361
|
+
|
|
362
|
+
#### 示例 1:添加配置系统
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
interface PluginConfig {
|
|
366
|
+
enabled: boolean;
|
|
367
|
+
priority: number;
|
|
368
|
+
dependencies?: string[];
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
class ConfigurablePlugin extends Dependency {
|
|
372
|
+
private config: PluginConfig = {
|
|
373
|
+
enabled: true,
|
|
374
|
+
priority: 0
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
constructor(filePath: string, config?: Partial<PluginConfig>) {
|
|
378
|
+
super(filePath);
|
|
379
|
+
if (config) {
|
|
380
|
+
this.config = { ...this.config, ...config };
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
getConfig(): PluginConfig {
|
|
385
|
+
return { ...this.config };
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
updateConfig(config: Partial<PluginConfig>): void {
|
|
389
|
+
this.config = { ...this.config, ...config };
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
isEnabled(): boolean {
|
|
393
|
+
return this.config.enabled;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
#### 示例 2:添加性能监控
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
class MonitoredPlugin extends Dependency {
|
|
402
|
+
private metrics = {
|
|
403
|
+
loadTime: 0,
|
|
404
|
+
mountTime: 0,
|
|
405
|
+
childCount: 0
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
async start(): Promise<void> {
|
|
409
|
+
const startTime = Date.now();
|
|
410
|
+
await super.start();
|
|
411
|
+
this.metrics.loadTime = Date.now() - startTime;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async mount(): Promise<void> {
|
|
415
|
+
const startTime = Date.now();
|
|
416
|
+
await super.mount();
|
|
417
|
+
this.metrics.mountTime = Date.now() - startTime;
|
|
418
|
+
this.metrics.childCount = this.children.length;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
getMetrics() {
|
|
422
|
+
return { ...this.metrics };
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
printMetrics(): void {
|
|
426
|
+
console.log(`📊 ${this.name} 性能指标:`);
|
|
427
|
+
console.log(` 加载时间: ${this.metrics.loadTime}ms`);
|
|
428
|
+
console.log(` 挂载时间: ${this.metrics.mountTime}ms`);
|
|
429
|
+
console.log(` 子节点数: ${this.metrics.childCount}`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### 工作原理
|
|
435
|
+
|
|
436
|
+
当父节点导入子模块时,`importChild()` 方法会自动使用 `this.constructor` 来创建子节点:
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
async importChild(importPath: string): Promise<Dependency> {
|
|
440
|
+
const absolutePath = this.resolveImportPath(this.#filePath, importPath);
|
|
441
|
+
|
|
442
|
+
// 使用父节点的构造函数创建子节点
|
|
443
|
+
const child = new (this.constructor as typeof Dependency)(absolutePath);
|
|
444
|
+
|
|
445
|
+
child.parent = this;
|
|
446
|
+
this.children.push(child);
|
|
447
|
+
await child.start();
|
|
448
|
+
|
|
449
|
+
return child;
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
这确保了:
|
|
454
|
+
- ✅ 子节点使用与父节点相同的类
|
|
455
|
+
- ✅ 整个依赖树保持类型一致
|
|
456
|
+
- ✅ 自定义属性和方法在所有节点上可用
|
|
457
|
+
|
|
458
|
+
### 注意事项
|
|
459
|
+
|
|
460
|
+
#### 1. 构造函数参数
|
|
461
|
+
|
|
462
|
+
如果你的自定义类需要额外的构造函数参数,需要确保只使用 `filePath` 作为必需参数:
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
// ✅ 正确:额外参数都是可选的
|
|
466
|
+
class MyPlugin extends Dependency {
|
|
467
|
+
constructor(filePath: string, config?: MyConfig) {
|
|
468
|
+
super(filePath);
|
|
469
|
+
// ...
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// ❌ 错误:必需的额外参数会导致子节点创建失败
|
|
474
|
+
class MyPlugin extends Dependency {
|
|
475
|
+
constructor(filePath: string, config: MyConfig) { // config 是必需的
|
|
476
|
+
super(filePath);
|
|
477
|
+
// ...
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
**解决方案:** 使用默认值或可选参数:
|
|
483
|
+
|
|
484
|
+
```typescript
|
|
485
|
+
class MyPlugin extends Dependency {
|
|
486
|
+
constructor(
|
|
487
|
+
filePath: string,
|
|
488
|
+
config: MyConfig = { /* 默认值 */ }
|
|
489
|
+
) {
|
|
490
|
+
super(filePath);
|
|
491
|
+
// ...
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
#### 2. 异步初始化
|
|
497
|
+
|
|
498
|
+
如果需要异步初始化,使用生命周期方法而不是构造函数:
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
class AsyncPlugin extends Dependency {
|
|
502
|
+
private initialized: boolean = false;
|
|
503
|
+
|
|
504
|
+
// ✅ 使用 start 方法
|
|
505
|
+
async start(): Promise<void> {
|
|
506
|
+
await this.initialize();
|
|
507
|
+
await super.start();
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
private async initialize(): Promise<void> {
|
|
511
|
+
// 异步初始化逻辑
|
|
512
|
+
this.initialized = true;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
## 🔌 插件生态系统
|
|
518
|
+
|
|
519
|
+
### 支持多种命名规范
|
|
520
|
+
|
|
521
|
+
#### 1. 社区插件(前缀命名)
|
|
522
|
+
|
|
523
|
+
```
|
|
524
|
+
zhin.js-plugin1
|
|
525
|
+
zhin.js-my-plugin
|
|
526
|
+
zhin.js-awesome-feature
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
#### 2. 官方插件(组织命名)
|
|
530
|
+
|
|
531
|
+
```
|
|
532
|
+
@zhin.js/core
|
|
533
|
+
@zhin.js/plugin1
|
|
534
|
+
@zhin.js/database
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
### 配置方法
|
|
538
|
+
|
|
539
|
+
#### 方法 1:环境变量(推荐)
|
|
540
|
+
|
|
541
|
+
在 `.env` 文件或启动脚本中设置:
|
|
542
|
+
|
|
543
|
+
```bash
|
|
544
|
+
# 同时支持两种插件
|
|
545
|
+
DEPENDENCY_TREE_INCLUDE=node_modules/zhin.js-,node_modules/@zhin.js/
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
#### 方法 2:package.json 脚本
|
|
549
|
+
|
|
550
|
+
```json
|
|
551
|
+
{
|
|
552
|
+
"scripts": {
|
|
553
|
+
"dev": "DEPENDENCY_TREE_INCLUDE=node_modules/zhin.js-,node_modules/@zhin.js/ bun src/index.ts",
|
|
554
|
+
"start": "DEPENDENCY_TREE_INCLUDE=node_modules/zhin.js-,node_modules/@zhin.js/ tsx src/index.ts"
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
#### 方法 3:使用 dotenv
|
|
560
|
+
|
|
561
|
+
```bash
|
|
562
|
+
# .env
|
|
563
|
+
DEPENDENCY_TREE_INCLUDE=node_modules/zhin.js-,node_modules/@zhin.js/
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
// index.ts
|
|
568
|
+
import 'dotenv/config';
|
|
569
|
+
import { Dependency } from '@zhin.js/dependency';
|
|
570
|
+
|
|
571
|
+
const root = new Dependency('./entry.js');
|
|
572
|
+
await root.start();
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### 实际场景
|
|
576
|
+
|
|
577
|
+
#### 场景 1:纯官方插件生态
|
|
578
|
+
|
|
579
|
+
如果你的项目只使用官方插件(如 `@zhin.js/*`):
|
|
580
|
+
|
|
581
|
+
```bash
|
|
582
|
+
DEPENDENCY_TREE_INCLUDE=node_modules/@zhin.js/
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
#### 场景 2:纯社区插件生态
|
|
586
|
+
|
|
587
|
+
如果你的项目只使用社区插件(如 `zhin.js-*`):
|
|
588
|
+
|
|
589
|
+
```bash
|
|
590
|
+
DEPENDENCY_TREE_INCLUDE=node_modules/zhin.js-
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
#### 场景 3:混合生态 ⭐
|
|
594
|
+
|
|
595
|
+
同时支持官方和社区插件(推荐):
|
|
596
|
+
|
|
597
|
+
```bash
|
|
598
|
+
DEPENDENCY_TREE_INCLUDE=node_modules/zhin.js-,node_modules/@zhin.js/
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
#### 场景 4:本地插件 + npm 插件
|
|
602
|
+
|
|
603
|
+
同时支持本地开发和 npm 安装的插件:
|
|
604
|
+
|
|
605
|
+
```bash
|
|
606
|
+
DEPENDENCY_TREE_INCLUDE=src/plugins,node_modules/zhin.js-,node_modules/@zhin.js/
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
#### 场景 5:选择性包含
|
|
610
|
+
|
|
611
|
+
只包含特定的插件:
|
|
612
|
+
|
|
613
|
+
```bash
|
|
614
|
+
DEPENDENCY_TREE_INCLUDE=node_modules/@zhin.js/core,node_modules/zhin.js-auth,node_modules/zhin.js-database
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
### 排除特定插件
|
|
618
|
+
|
|
619
|
+
使用 `DEPENDENCY_TREE_EXCLUDE` 排除不需要的插件:
|
|
620
|
+
|
|
621
|
+
```bash
|
|
622
|
+
# 包含所有 zhin.js 插件,但排除测试和开发插件
|
|
623
|
+
DEPENDENCY_TREE_INCLUDE=node_modules/zhin.js-,node_modules/@zhin.js/
|
|
624
|
+
DEPENDENCY_TREE_EXCLUDE=node_modules/zhin.js-dev,node_modules/@zhin.js/testing
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
### 发布插件为 npm 包
|
|
628
|
+
|
|
629
|
+
#### 插件包作者(发布方)
|
|
630
|
+
|
|
631
|
+
在你的插件包 README 中说明:
|
|
632
|
+
|
|
633
|
+
```markdown
|
|
634
|
+
## 使用方法
|
|
635
|
+
|
|
636
|
+
安装插件:
|
|
637
|
+
|
|
638
|
+
\`\`\`bash
|
|
639
|
+
npm install @your-org/your-plugin
|
|
640
|
+
\`\`\`
|
|
641
|
+
|
|
642
|
+
配置环境变量以启用依赖树转换:
|
|
643
|
+
|
|
644
|
+
\`\`\`bash
|
|
645
|
+
DEPENDENCY_TREE_INCLUDE=node_modules/@your-org/your-plugin
|
|
646
|
+
\`\`\`
|
|
647
|
+
|
|
648
|
+
或者在 `package.json` 中:
|
|
649
|
+
|
|
650
|
+
\`\`\`json
|
|
651
|
+
{
|
|
652
|
+
"scripts": {
|
|
653
|
+
"start": "DEPENDENCY_TREE_INCLUDE=node_modules/@your-org/your-plugin tsx src/index.ts"
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
\`\`\`
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
#### 插件使用者
|
|
660
|
+
|
|
661
|
+
```bash
|
|
662
|
+
# .env 文件
|
|
663
|
+
DEPENDENCY_TREE_INCLUDE=node_modules/@my-org/plugin1,node_modules/@my-org/plugin2
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
或者在启动命令中:
|
|
667
|
+
|
|
668
|
+
```json
|
|
669
|
+
{
|
|
670
|
+
"scripts": {
|
|
671
|
+
"start": "DEPENDENCY_TREE_INCLUDE=node_modules/@my-org/my-plugin tsx src/index.ts"
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
## 📚 API 文档
|
|
677
|
+
|
|
678
|
+
### `Dependency` 类
|
|
679
|
+
|
|
680
|
+
#### 构造函数
|
|
681
|
+
|
|
682
|
+
```typescript
|
|
683
|
+
new Dependency(filePath: string)
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
#### 方法
|
|
687
|
+
|
|
688
|
+
- `async start()` - 启动依赖(导入模块并构建树)
|
|
689
|
+
- `async mount()` - 挂载(执行 onMount hooks)
|
|
690
|
+
- `async dispose()` - 卸载(执行 onDispose hooks)
|
|
691
|
+
- `async stop()` - 停止(dispose 并级联卸载子节点)
|
|
692
|
+
- `async reload(): Promise<Dependency>` - 热重载,返回新的 Dependency 实例(支持根节点)
|
|
693
|
+
- `printTree(prefix?, showListeners?, showPaths?)` - 打印树结构
|
|
694
|
+
- `toJSON()` - 导出为 JSON
|
|
695
|
+
- `dispatch(event, ...args)` - 触发当前节点的事件
|
|
696
|
+
- `broadcast(event, ...args)` - 广播事件到整个子树
|
|
697
|
+
|
|
698
|
+
#### 属性
|
|
699
|
+
|
|
700
|
+
- `name` - 依赖名称
|
|
701
|
+
- `filePath` - 文件路径
|
|
702
|
+
- `parent` - 父依赖
|
|
703
|
+
- `children` - 子依赖数组
|
|
704
|
+
|
|
705
|
+
#### 继承自 EventEmitter
|
|
706
|
+
|
|
707
|
+
```typescript
|
|
708
|
+
// 监听事件
|
|
709
|
+
dep.on('afterMount', (dep) => console.log('挂载完成'));
|
|
710
|
+
|
|
711
|
+
// 触发事件
|
|
712
|
+
dep.emit('custom-event', data);
|
|
713
|
+
|
|
714
|
+
// 其他 EventEmitter 方法
|
|
715
|
+
dep.once(event, listener);
|
|
716
|
+
dep.off(event, listener);
|
|
717
|
+
dep.removeAllListeners(event);
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
## 🎨 生命周期
|
|
721
|
+
|
|
722
|
+
```
|
|
723
|
+
┌─────────────┐
|
|
724
|
+
│ create │ new Dependency()
|
|
725
|
+
└──────┬──────┘
|
|
726
|
+
│
|
|
727
|
+
┌──────▼──────┐
|
|
728
|
+
│ start │ 导入模块,构建树
|
|
729
|
+
└──────┬──────┘
|
|
730
|
+
│
|
|
731
|
+
┌──────▼──────┐
|
|
732
|
+
│ mount │ 执行 onMount hooks
|
|
733
|
+
└──────┬──────┘
|
|
734
|
+
│
|
|
735
|
+
┌───▼────┐
|
|
736
|
+
│ active │ 运行中...
|
|
737
|
+
└───┬────┘
|
|
738
|
+
│
|
|
739
|
+
┌──────▼──────┐
|
|
740
|
+
│ dispose │ 执行 onDispose hooks
|
|
741
|
+
└──────┬──────┘
|
|
742
|
+
│
|
|
743
|
+
┌──────▼──────┐
|
|
744
|
+
│ stop │ 级联卸载子节点
|
|
745
|
+
└─────────────┘
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
### 生命周期事件
|
|
749
|
+
|
|
750
|
+
Dependency 类继承自 EventEmitter,在生命周期的各个阶段会触发相应事件:
|
|
751
|
+
|
|
752
|
+
- `beforeStart` - 开始启动前
|
|
753
|
+
- `afterStart` - 启动完成后
|
|
754
|
+
- `beforeMount` - 开始挂载前
|
|
755
|
+
- `afterMount` - 挂载完成后
|
|
756
|
+
- `beforeDispose` - 开始卸载前
|
|
757
|
+
- `afterDispose` - 卸载完成后
|
|
758
|
+
- `beforeReload` - 开始重载前
|
|
759
|
+
- `afterReload` - 重载完成后
|
|
760
|
+
- `fileChange` - 文件变更时
|
|
761
|
+
- `error` - 发生错误时
|
|
762
|
+
|
|
763
|
+
## 🤝 贡献
|
|
764
|
+
|
|
765
|
+
欢迎提交 Issue 和 Pull Request!
|
|
766
|
+
|
|
767
|
+
## 📄 许可证
|
|
768
|
+
|
|
769
|
+
MIT
|