@zhin.js/dependency 1.0.1 → 1.0.3
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/README.md +414 -613
- package/dist/dependency.d.ts +18 -12
- package/dist/dependency.d.ts.map +1 -1
- package/dist/dependency.js +196 -131
- package/dist/dependency.js.map +1 -1
- package/dist/hook-registry.d.ts +4 -2
- package/dist/hook-registry.d.ts.map +1 -1
- package/dist/hook-registry.js +11 -4
- package/dist/hook-registry.js.map +1 -1
- package/dist/hooks.d.ts +4 -2
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +18 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/loaders/bun-plugin.ts +8 -5
- package/loaders/transform-utils.mjs +108 -27
- package/loaders/tsx-loader.mjs +4 -3
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -1,769 +1,570 @@
|
|
|
1
|
-
#
|
|
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
|
-
- 🧬 **继承支持** - 完全支持类继承,创建自定义插件类
|
|
1
|
+
# @zhin.js/dependency
|
|
34
2
|
|
|
35
|
-
|
|
3
|
+
基于运行时依赖树分析的模块管理系统,提供自动依赖去重、热重载和生命周期管理功能。
|
|
36
4
|
|
|
37
|
-
|
|
38
|
-
pnpm add @zhin.js/dependency
|
|
39
|
-
```
|
|
5
|
+
## ✨ 核心特性
|
|
40
6
|
|
|
41
|
-
|
|
7
|
+
### 1. **自动依赖去重**
|
|
8
|
+
- 全局唯一实例:同一文件路径在整个依赖树中只有一个实例
|
|
9
|
+
- 引用计数:通过 `refs` 集合追踪所有引用者
|
|
10
|
+
- 智能共享:自动识别共享依赖并显示引用计数
|
|
42
11
|
|
|
43
|
-
###
|
|
12
|
+
### 2. **热重载 (Hot Reload)**
|
|
13
|
+
- 原地重载:根节点重载时保持引用不变
|
|
14
|
+
- 智能 Diff:自动比较新旧子依赖,保留未变化的子树
|
|
15
|
+
- 状态保持:共享依赖在重载时保持状态和监听器
|
|
44
16
|
|
|
45
|
-
|
|
46
|
-
|
|
17
|
+
### 3. **生命周期管理**
|
|
18
|
+
- 细粒度生命周期钩子
|
|
19
|
+
- 事件冒泡机制:从叶子节点向根节点传播
|
|
20
|
+
- 自动清理:停止时自动清理子依赖和副作用
|
|
47
21
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
22
|
+
### 4. **副作用自动管理**
|
|
23
|
+
- 自动包装全局副作用函数(`setInterval`, `setTimeout`, `setImmediate`)
|
|
24
|
+
- 模块卸载时自动清理副作用
|
|
25
|
+
- 通过环境变量 `DEPENDENCY_WRAP_EFFECTS` 控制开关
|
|
51
26
|
|
|
52
|
-
|
|
53
|
-
console.log(root.printTree('', true, true));
|
|
27
|
+
## 📦 安装
|
|
54
28
|
|
|
55
|
-
|
|
56
|
-
|
|
29
|
+
```bash
|
|
30
|
+
pnpm add @zhin.js/dependency
|
|
57
31
|
```
|
|
58
32
|
|
|
59
|
-
|
|
33
|
+
## 🚀 快速开始
|
|
34
|
+
|
|
35
|
+
### 基础用法
|
|
60
36
|
|
|
61
37
|
```typescript
|
|
62
|
-
|
|
63
|
-
import { onMount, onDispose, addListener } from '@zhin.js/dependency';
|
|
38
|
+
import { Dependency, onMount, onDispose, getCurrentDependency } from '@zhin.js/dependency';
|
|
64
39
|
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
console.log('插件已挂载!');
|
|
68
|
-
});
|
|
40
|
+
// 插件代码 (plugin.ts)
|
|
41
|
+
export const name = 'my-plugin';
|
|
69
42
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
console.log('事件触发');
|
|
43
|
+
onMount(() => {
|
|
44
|
+
console.log('插件已挂载');
|
|
73
45
|
});
|
|
74
46
|
|
|
75
|
-
// 卸载钩子
|
|
76
47
|
onDispose(() => {
|
|
77
|
-
unsubscribe();
|
|
78
48
|
console.log('插件已卸载');
|
|
79
49
|
});
|
|
80
50
|
|
|
81
|
-
//
|
|
82
|
-
|
|
51
|
+
// 导入子依赖
|
|
52
|
+
const dep = getCurrentDependency();
|
|
53
|
+
if (dep) {
|
|
54
|
+
await dep.importChild('./child-plugin');
|
|
55
|
+
}
|
|
83
56
|
|
|
84
|
-
|
|
85
|
-
|
|
57
|
+
// 主程序
|
|
58
|
+
const root = new Dependency('./plugin.ts');
|
|
59
|
+
await root.start();
|
|
60
|
+
|
|
61
|
+
console.log(root.printTree('', true, true));
|
|
62
|
+
// my-plugin (0 listeners)
|
|
63
|
+
// └── child-plugin (0 listeners)
|
|
86
64
|
|
|
87
|
-
|
|
65
|
+
await root.stop();
|
|
66
|
+
```
|
|
88
67
|
|
|
89
|
-
|
|
68
|
+
### 热重载
|
|
90
69
|
|
|
91
70
|
```typescript
|
|
92
71
|
import { Dependency } from '@zhin.js/dependency';
|
|
72
|
+
import chokidar from 'chokidar';
|
|
93
73
|
|
|
94
|
-
|
|
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');
|
|
74
|
+
const root = new Dependency('./plugin.ts');
|
|
108
75
|
await root.start();
|
|
109
76
|
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
console.log(
|
|
77
|
+
// 监听文件变化
|
|
78
|
+
chokidar.watch('./plugin.ts').on('change', async () => {
|
|
79
|
+
console.log('🔄 检测到文件变化,重载中...');
|
|
80
|
+
await root.reload();
|
|
81
|
+
console.log('✅ 重载完成');
|
|
82
|
+
});
|
|
113
83
|
```
|
|
114
84
|
|
|
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
|
-
```
|
|
85
|
+
## 🔧 核心 API
|
|
131
86
|
|
|
132
|
-
|
|
87
|
+
### Dependency 类
|
|
133
88
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
89
|
+
#### 构造函数
|
|
90
|
+
```typescript
|
|
91
|
+
constructor(filePath: string)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### 生命周期方法
|
|
95
|
+
|
|
96
|
+
| 方法 | 说明 | 返回值 |
|
|
97
|
+
|------|------|--------|
|
|
98
|
+
| `start()` | 启动依赖:初始化模块、挂载、启动子依赖 | `Promise<void>` |
|
|
99
|
+
| `mount()` | 挂载:执行 `onMount` 钩子 | `Promise<void>` |
|
|
100
|
+
| `dispose()` | 卸载:执行 `onDispose` 钩子和副作用清理 | `Promise<void>` |
|
|
101
|
+
| `stop()` | 停止:卸载、清理缓存、递归停止子依赖 | `Promise<void>` |
|
|
102
|
+
| `reload()` | 重载:卸载、清理、重新导入、Diff 子依赖 | `Promise<Dependency>` |
|
|
103
|
+
|
|
104
|
+
#### 依赖管理方法
|
|
105
|
+
|
|
106
|
+
| 方法 | 说明 | 返回值 |
|
|
107
|
+
|------|------|--------|
|
|
108
|
+
| `importChild(path)` | 导入子依赖(自动去重) | `Promise<P>` |
|
|
109
|
+
| `removeChild(child)` | 移除子依赖(引用计数减 1) | `Promise<void>` |
|
|
110
|
+
| `init()` | 初始化模块(导入代码并注册到全局池) | `Promise<void>` |
|
|
111
|
+
|
|
112
|
+
#### 属性 & Getter
|
|
113
|
+
|
|
114
|
+
| 属性 | 类型 | 说明 |
|
|
115
|
+
|------|------|------|
|
|
116
|
+
| `name` | `string` | 依赖名称(从文件名提取) |
|
|
117
|
+
| `filePath` | `string` | 文件绝对路径 |
|
|
118
|
+
| `children` | `P[]` | 子依赖列表(通过 Symbol 实现) |
|
|
119
|
+
| `refs` | `Set<string>` | 引用者文件路径集合 |
|
|
120
|
+
| `parent` | `P \| null` | 父依赖(`refs` 的第一个元素) |
|
|
121
|
+
| `root` | `P` | 根依赖 |
|
|
122
|
+
| `isRoot` | `boolean` | 是否为根节点(`refs.size === 0`) |
|
|
123
|
+
| `started` | `boolean` | 是否已启动 |
|
|
124
|
+
| `mounted` | `boolean` | 是否已挂载 |
|
|
125
|
+
| `reloading` | `boolean` | 是否正在重载 |
|
|
126
|
+
|
|
127
|
+
#### 工具方法
|
|
128
|
+
|
|
129
|
+
| 方法 | 说明 | 返回值 |
|
|
130
|
+
|------|------|--------|
|
|
131
|
+
| `getPath()` | 获取从根到当前节点的路径 | `Dependency[]` |
|
|
132
|
+
| `getDepth()` | 获取当前节点深度 | `number` |
|
|
133
|
+
| `printTree()` | 打印依赖树 | `string` |
|
|
134
|
+
| `toJSON()` | 序列化为 JSON | `object` |
|
|
135
|
+
|
|
136
|
+
### Hooks API
|
|
137
|
+
|
|
138
|
+
#### onMount
|
|
139
|
+
在依赖挂载时执行
|
|
140
|
+
```typescript
|
|
141
|
+
onMount(() => {
|
|
142
|
+
console.log('已挂载');
|
|
143
|
+
});
|
|
137
144
|
```
|
|
138
145
|
|
|
139
|
-
|
|
146
|
+
#### onDispose
|
|
147
|
+
在依赖卸载时执行
|
|
148
|
+
```typescript
|
|
149
|
+
onDispose(() => {
|
|
150
|
+
console.log('已卸载');
|
|
151
|
+
});
|
|
140
152
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
153
|
+
// 内部清理(在生命周期监听器清理前执行)
|
|
154
|
+
onDispose(() => {
|
|
155
|
+
console.log('内部清理');
|
|
156
|
+
}, true);
|
|
144
157
|
```
|
|
145
158
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
|
|
159
|
+
#### getCurrentDependency
|
|
160
|
+
获取当前模块的 Dependency 实例
|
|
161
|
+
```typescript
|
|
162
|
+
const dep = getCurrentDependency();
|
|
163
|
+
if (dep) {
|
|
164
|
+
await dep.importChild('./child');
|
|
165
|
+
}
|
|
151
166
|
```
|
|
152
167
|
|
|
153
|
-
|
|
168
|
+
## 📋 生命周期详解
|
|
154
169
|
|
|
155
|
-
|
|
170
|
+
### 启动流程 (start)
|
|
156
171
|
|
|
157
|
-
```bash
|
|
158
|
-
# 排除测试文件
|
|
159
|
-
DEPENDENCY_TREE_EXCLUDE=plugins/__tests__,plugins/**/*.test.ts
|
|
160
172
|
```
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
}
|
|
173
|
+
1. before-start ──▶ 向上冒泡
|
|
174
|
+
2. self.start ──▶ 本节点监听器
|
|
175
|
+
3. init() ──▶ 导入模块代码(如果未初始化)
|
|
176
|
+
4. mount() ──▶ 挂载钩子
|
|
177
|
+
├─ before-mount ──▶ 向上冒泡
|
|
178
|
+
├─ self.mounted ──▶ onMount 钩子
|
|
179
|
+
└─ mounted ──▶ 向上冒泡
|
|
180
|
+
5. children.start() ──▶ 递归启动子依赖
|
|
181
|
+
6. started ──▶ 向上冒泡
|
|
172
182
|
```
|
|
173
183
|
|
|
174
|
-
|
|
184
|
+
### 卸载流程 (dispose)
|
|
175
185
|
|
|
176
|
-
```json
|
|
177
|
-
{
|
|
178
|
-
"scripts": {
|
|
179
|
-
"start": "tsx --import @zhin.js/dependency/register.mjs src/index.ts"
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
186
|
```
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
{
|
|
188
|
-
"scripts": {
|
|
189
|
-
"build": "tsc",
|
|
190
|
-
"start": "node --import @zhin.js/dependency/register.mjs dist/index.js"
|
|
191
|
-
}
|
|
192
|
-
}
|
|
187
|
+
1. before-dispose ──▶ 向上冒泡
|
|
188
|
+
2. self.dispose ──▶ onDispose 钩子
|
|
189
|
+
3. #onSelfDispose ──▶ 内部副作用清理
|
|
190
|
+
4. disposed ──▶ 向上冒泡
|
|
193
191
|
```
|
|
194
192
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
### 自动类型推断
|
|
193
|
+
### 停止流程 (stop)
|
|
198
194
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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');
|
|
195
|
+
```
|
|
196
|
+
检查: refs.size > 0 ? 返回(还有引用者)
|
|
197
|
+
├─ before-stop ──▶ 向上冒泡
|
|
198
|
+
├─ self.stop ──▶ 本节点监听器
|
|
199
|
+
├─ dispose() ──▶ 卸载
|
|
200
|
+
├─ 清理全局池 ──▶ globalDepMap.delete()
|
|
201
|
+
├─ 清理模块缓存 ──▶ removeModuleCache()
|
|
202
|
+
├─ removeChild() ──▶ 递归移除子依赖(refs-1)
|
|
203
|
+
├─ stopped ──▶ 向上冒泡
|
|
204
|
+
└─ started = false
|
|
225
205
|
```
|
|
226
206
|
|
|
227
|
-
###
|
|
207
|
+
### 重载流程 (reload)
|
|
228
208
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
209
|
+
```
|
|
210
|
+
1. before-reload ──▶ 向上冒泡
|
|
211
|
+
2. self.reload ──▶ 本节点监听器
|
|
212
|
+
3. reloading ──▶ 向上冒泡
|
|
213
|
+
4. 保存子依赖 ──▶ savedChildren = [...this.children]
|
|
233
214
|
|
|
234
|
-
|
|
215
|
+
5. #cleanupBeforeReload()
|
|
216
|
+
├─ dispose() ──▶ 卸载
|
|
217
|
+
├─ parent?.removeChild(this) ──▶ 从父节点移除
|
|
218
|
+
└─ removeModuleCache() ──▶ 清理缓存
|
|
235
219
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
220
|
+
6. #reloadNode()
|
|
221
|
+
├─ 如果是根节点:
|
|
222
|
+
│ ├─ this.#cleanLifecycleListeners() ──▶ 清理生命周期监听器
|
|
223
|
+
│ ├─ this[childrenKey].clear() ──▶ 清空子依赖
|
|
224
|
+
│ ├─ await this.init() ──▶ 重新导入模块
|
|
225
|
+
│ └─ return this ──▶ 返回自己
|
|
226
|
+
└─ 如果有父节点:
|
|
227
|
+
└─ return await this.parent.importChild(path) ──▶ 父节点重新导入
|
|
241
228
|
|
|
242
|
-
|
|
229
|
+
7. #updateChildren(newNode, savedChildren)
|
|
230
|
+
├─ #diffChildren() ──▶ 比较新旧子依赖
|
|
231
|
+
│ ├─ removedChildren ──▶ 旧的但新的没有
|
|
232
|
+
│ └─ addedChildren ──▶ 新的但旧的没有
|
|
233
|
+
├─ #removeChildren() ──▶ 移除已删除的子依赖
|
|
234
|
+
├─ #addChildren() ──▶ 添加新增的子依赖
|
|
235
|
+
└─ 更新 childrenKey ──▶ 用 savedChildren 覆盖
|
|
243
236
|
|
|
244
|
-
|
|
237
|
+
8. await newNode.start() ──▶ 启动新节点
|
|
238
|
+
9. reloaded ──▶ 向上冒泡
|
|
239
|
+
```
|
|
245
240
|
|
|
246
|
-
|
|
241
|
+
## 🎯 核心机制详解
|
|
247
242
|
|
|
248
|
-
|
|
243
|
+
### 1. 依赖去重机制
|
|
249
244
|
|
|
245
|
+
#### 全局依赖池
|
|
250
246
|
```typescript
|
|
251
|
-
|
|
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
|
-
});
|
|
247
|
+
private static globalDepMap = new Map<string, Dependency>();
|
|
294
248
|
```
|
|
249
|
+
- **Key**: 文件绝对路径(标准化后)
|
|
250
|
+
- **Value**: Dependency 实例
|
|
251
|
+
- **作用**: 确保同一文件只有一个实例
|
|
295
252
|
|
|
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
|
-
|
|
253
|
+
#### 引用计数 (refs)
|
|
335
254
|
```typescript
|
|
336
|
-
|
|
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
|
|
255
|
+
public refs: Set<string> = new Set();
|
|
358
256
|
```
|
|
257
|
+
- 存储所有引用者的文件路径
|
|
258
|
+
- 首次导入者也在 `refs` 中
|
|
259
|
+
- `refs.size` 即为总引用计数
|
|
359
260
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
#### 示例 1:添加配置系统
|
|
363
|
-
|
|
261
|
+
#### parent (Getter)
|
|
364
262
|
```typescript
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
263
|
+
get parent(): P | null {
|
|
264
|
+
return this.refs.size > 0
|
|
265
|
+
? Dependency.globalDepMap.get(this.refs.values().next().value!) as P
|
|
266
|
+
: null;
|
|
369
267
|
}
|
|
268
|
+
```
|
|
269
|
+
- 动态计算,返回 `refs` 的第一个元素
|
|
270
|
+
- 代表首次导入者
|
|
271
|
+
- Set 迭代顺序稳定,保证 `parent` 始终是第一个
|
|
370
272
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
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;
|
|
273
|
+
#### importChild 逻辑
|
|
274
|
+
```typescript
|
|
275
|
+
async importChild(importPath: string): Promise<P> {
|
|
276
|
+
const normalizedPath = this.resolveFilePath(absolutePath);
|
|
277
|
+
let child = Dependency.globalDepMap.get(normalizedPath);
|
|
278
|
+
|
|
279
|
+
if (!child) {
|
|
280
|
+
// 首次导入:创建实例并初始化
|
|
281
|
+
child = new (this.constructor as Constructor<P>)(normalizedPath);
|
|
282
|
+
await child.init(); // 导入模块代码
|
|
394
283
|
}
|
|
284
|
+
|
|
285
|
+
// 建立引用关系
|
|
286
|
+
child.refs.add(this.#filePath);
|
|
287
|
+
this[childrenKey].add(child.filePath);
|
|
288
|
+
|
|
289
|
+
return child;
|
|
395
290
|
}
|
|
396
291
|
```
|
|
397
292
|
|
|
398
|
-
####
|
|
399
|
-
|
|
293
|
+
#### removeChild 逻辑
|
|
400
294
|
```typescript
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
async start(): Promise<void> {
|
|
409
|
-
const startTime = Date.now();
|
|
410
|
-
await super.start();
|
|
411
|
-
this.metrics.loadTime = Date.now() - startTime;
|
|
295
|
+
async removeChild(child: P): Promise<void> {
|
|
296
|
+
child.refs.delete(this.#filePath);
|
|
297
|
+
this[childrenKey].delete(child.filePath);
|
|
298
|
+
|
|
299
|
+
if (!child.refs.size) {
|
|
300
|
+
await child.stop(); // 引用计数归零,停止
|
|
412
301
|
}
|
|
302
|
+
}
|
|
303
|
+
```
|
|
413
304
|
|
|
414
|
-
|
|
415
|
-
const startTime = Date.now();
|
|
416
|
-
await super.mount();
|
|
417
|
-
this.metrics.mountTime = Date.now() - startTime;
|
|
418
|
-
this.metrics.childCount = this.children.length;
|
|
419
|
-
}
|
|
305
|
+
### 2. 热重载机制
|
|
420
306
|
|
|
421
|
-
|
|
422
|
-
return { ...this.metrics };
|
|
423
|
-
}
|
|
307
|
+
#### Clone-Diff-Merge 策略
|
|
424
308
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
console.log(` 挂载时间: ${this.metrics.mountTime}ms`);
|
|
429
|
-
console.log(` 子节点数: ${this.metrics.childCount}`);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
309
|
+
**根节点重载**:
|
|
310
|
+
```
|
|
311
|
+
保存 children → dispose → 清理 → 重新 init → diff → 恢复 children → start
|
|
432
312
|
```
|
|
433
313
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
314
|
+
**非根节点重载**:
|
|
315
|
+
```
|
|
316
|
+
保存 children → dispose → 父节点重新 importChild → diff → 恢复 children → start
|
|
317
|
+
```
|
|
437
318
|
|
|
319
|
+
#### Diff 算法
|
|
438
320
|
```typescript
|
|
439
|
-
|
|
440
|
-
|
|
321
|
+
#diffChildren(newNode, savedChildren) {
|
|
322
|
+
// Removed: 在 saved 中但不在 new 中
|
|
323
|
+
const removedChildren = savedChildren.filter(
|
|
324
|
+
child => !newNode.children.find(c => c.filePath === child.filePath)
|
|
325
|
+
);
|
|
441
326
|
|
|
442
|
-
//
|
|
443
|
-
const
|
|
327
|
+
// Added: 在 new 中但不在 saved 中
|
|
328
|
+
const addedChildren = newNode.children.filter(
|
|
329
|
+
child => !savedChildren.find(c => c.filePath === child.filePath)
|
|
330
|
+
);
|
|
444
331
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
await child.start();
|
|
448
|
-
|
|
449
|
-
return child;
|
|
332
|
+
// Kept: 都存在的会被自动保留(不在 removed 中)
|
|
333
|
+
return { removedChildren, addedChildren };
|
|
450
334
|
}
|
|
451
335
|
```
|
|
452
336
|
|
|
453
|
-
|
|
454
|
-
-
|
|
455
|
-
-
|
|
456
|
-
-
|
|
457
|
-
|
|
458
|
-
### 注意事项
|
|
459
|
-
|
|
460
|
-
#### 1. 构造函数参数
|
|
337
|
+
#### 状态保持
|
|
338
|
+
- **Kept 子依赖**: 不重新创建,保持原实例
|
|
339
|
+
- **Added 子依赖**: 新创建或从全局池复用
|
|
340
|
+
- **Removed 子依赖**: 调用 `removeChild`,引用计数减 1
|
|
461
341
|
|
|
462
|
-
|
|
342
|
+
### 3. children 的 Symbol 实现
|
|
463
343
|
|
|
464
344
|
```typescript
|
|
465
|
-
|
|
466
|
-
class MyPlugin extends Dependency {
|
|
467
|
-
constructor(filePath: string, config?: MyConfig) {
|
|
468
|
-
super(filePath);
|
|
469
|
-
// ...
|
|
470
|
-
}
|
|
471
|
-
}
|
|
345
|
+
const childrenKey = Symbol('children');
|
|
472
346
|
|
|
473
|
-
//
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
}
|
|
347
|
+
[childrenKey]: Set<string> = new Set(); // 存储子依赖的文件路径
|
|
348
|
+
|
|
349
|
+
get children(): P[] {
|
|
350
|
+
return Array.from(this[childrenKey])
|
|
351
|
+
.map(filePath => Dependency.globalDepMap.get(filePath) as P);
|
|
479
352
|
}
|
|
480
353
|
```
|
|
481
354
|
|
|
482
|
-
|
|
355
|
+
**优势**:
|
|
356
|
+
- 通过文件路径间接引用,避免循环引用
|
|
357
|
+
- 从全局池动态获取,保证始终是最新实例
|
|
358
|
+
- 支持 Diff 和更新操作
|
|
483
359
|
|
|
360
|
+
### 4. 事件系统
|
|
361
|
+
|
|
362
|
+
#### 事件冒泡
|
|
484
363
|
```typescript
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
super(filePath);
|
|
491
|
-
// ...
|
|
492
|
-
}
|
|
364
|
+
async dispatchAsync(event: string, ...args: any[]): Promise<void> {
|
|
365
|
+
if (this.parent)
|
|
366
|
+
await this.parent.dispatchAsync(event, ...args);
|
|
367
|
+
else
|
|
368
|
+
await this.broadcastAsync(event, ...args);
|
|
493
369
|
}
|
|
494
370
|
```
|
|
371
|
+
- 有父节点:向父节点传播
|
|
372
|
+
- 无父节点(根节点):广播到整个子树
|
|
495
373
|
|
|
496
|
-
####
|
|
497
|
-
|
|
498
|
-
如果需要异步初始化,使用生命周期方法而不是构造函数:
|
|
499
|
-
|
|
374
|
+
#### 事件广播
|
|
500
375
|
```typescript
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
//
|
|
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;
|
|
376
|
+
async broadcastAsync(event: string, ...args: any[]): Promise<void> {
|
|
377
|
+
await this.emitAsync(event, ...args); // 触发自己的监听器
|
|
378
|
+
for (const child of this.children) {
|
|
379
|
+
await child.broadcastAsync(event, ...args); // 递归广播
|
|
513
380
|
}
|
|
514
381
|
}
|
|
515
382
|
```
|
|
516
383
|
|
|
517
|
-
## 🔌
|
|
518
|
-
|
|
519
|
-
### 支持多种命名规范
|
|
384
|
+
## 🔌 Loader 使用
|
|
520
385
|
|
|
521
|
-
|
|
386
|
+
### Tsx (Node.js)
|
|
522
387
|
|
|
523
|
-
```
|
|
524
|
-
zhin.js
|
|
525
|
-
zhin.js-my-plugin
|
|
526
|
-
zhin.js-awesome-feature
|
|
388
|
+
```bash
|
|
389
|
+
tsx --import @zhin.js/dependency/register.mjs index.ts
|
|
527
390
|
```
|
|
528
391
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
@zhin.js/
|
|
534
|
-
|
|
392
|
+
或在 `package.json` 中:
|
|
393
|
+
```json
|
|
394
|
+
{
|
|
395
|
+
"scripts": {
|
|
396
|
+
"dev": "tsx --import @zhin.js/dependency/register.mjs src/index.ts"
|
|
397
|
+
}
|
|
398
|
+
}
|
|
535
399
|
```
|
|
536
400
|
|
|
537
|
-
###
|
|
538
|
-
|
|
539
|
-
#### 方法 1:环境变量(推荐)
|
|
540
|
-
|
|
541
|
-
在 `.env` 文件或启动脚本中设置:
|
|
401
|
+
### Bun
|
|
542
402
|
|
|
543
403
|
```bash
|
|
544
|
-
|
|
545
|
-
DEPENDENCY_TREE_INCLUDE=node_modules/zhin.js-,node_modules/@zhin.js/
|
|
404
|
+
bun --preload @zhin.js/dependency/bun-preload.ts index.ts
|
|
546
405
|
```
|
|
547
406
|
|
|
548
|
-
|
|
549
|
-
|
|
407
|
+
或在 `package.json` 中:
|
|
550
408
|
```json
|
|
551
409
|
{
|
|
552
410
|
"scripts": {
|
|
553
|
-
"dev": "
|
|
554
|
-
"start": "DEPENDENCY_TREE_INCLUDE=node_modules/zhin.js-,node_modules/@zhin.js/ tsx src/index.ts"
|
|
411
|
+
"dev": "bun --preload @zhin.js/dependency/bun-preload.ts src/index.ts"
|
|
555
412
|
}
|
|
556
413
|
}
|
|
557
414
|
```
|
|
558
415
|
|
|
559
|
-
|
|
416
|
+
### 环境变量
|
|
560
417
|
|
|
561
418
|
```bash
|
|
562
|
-
#
|
|
563
|
-
|
|
564
|
-
```
|
|
565
|
-
|
|
419
|
+
# 禁用副作用自动管理
|
|
420
|
+
DEPENDENCY_WRAP_EFFECTS=false tsx --import @zhin.js/dependency/register.mjs index.ts
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
## 📊 生命周期事件完整列表
|
|
424
|
+
|
|
425
|
+
### 本节点事件 (self.*)
|
|
426
|
+
- `self.start`: 启动开始
|
|
427
|
+
- `self.mounted`: 挂载完成(onMount 钩子)
|
|
428
|
+
- `self.dispose`: 卸载开始(onDispose 钩子)
|
|
429
|
+
- `self.stop`: 停止开始
|
|
430
|
+
- `self.reload`: 重载开始
|
|
431
|
+
|
|
432
|
+
### 冒泡事件
|
|
433
|
+
- `before-start`: 启动前
|
|
434
|
+
- `started`: 启动完成
|
|
435
|
+
- `before-mount`: 挂载前
|
|
436
|
+
- `mounted`: 挂载完成
|
|
437
|
+
- `before-dispose`: 卸载前
|
|
438
|
+
- `disposed`: 卸载完成
|
|
439
|
+
- `before-stop`: 停止前
|
|
440
|
+
- `stopped`: 停止完成
|
|
441
|
+
- `before-reload`: 重载前
|
|
442
|
+
- `reloading`: 重载中
|
|
443
|
+
- `reloaded`: 重载完成
|
|
444
|
+
|
|
445
|
+
### 错误事件
|
|
446
|
+
- `error`: 通用错误
|
|
447
|
+
- `reload.error`: 重载错误
|
|
448
|
+
|
|
449
|
+
## 🎨 实用场景
|
|
450
|
+
|
|
451
|
+
### 插件系统
|
|
566
452
|
```typescript
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
453
|
+
class PluginDependency extends Dependency {
|
|
454
|
+
async enable() {
|
|
455
|
+
await this.start();
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
async disable() {
|
|
459
|
+
await this.stop();
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
async reload() {
|
|
463
|
+
return super.reload() as Promise<PluginDependency>;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
580
466
|
|
|
581
|
-
|
|
582
|
-
|
|
467
|
+
const plugin = new PluginDependency('./plugin.ts');
|
|
468
|
+
await plugin.enable();
|
|
583
469
|
```
|
|
584
470
|
|
|
585
|
-
|
|
471
|
+
### 微服务热重载
|
|
472
|
+
```typescript
|
|
473
|
+
import { Dependency } from '@zhin.js/dependency';
|
|
474
|
+
import chokidar from 'chokidar';
|
|
586
475
|
|
|
587
|
-
|
|
476
|
+
const services = new Map<string, Dependency>();
|
|
588
477
|
|
|
589
|
-
|
|
590
|
-
|
|
478
|
+
async function loadService(servicePath: string) {
|
|
479
|
+
const service = new Dependency(servicePath);
|
|
480
|
+
await service.start();
|
|
481
|
+
services.set(servicePath, service);
|
|
482
|
+
|
|
483
|
+
chokidar.watch(servicePath).on('change', async () => {
|
|
484
|
+
await service.reload();
|
|
485
|
+
});
|
|
486
|
+
}
|
|
591
487
|
```
|
|
592
488
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
489
|
+
### 依赖树可视化
|
|
490
|
+
```typescript
|
|
491
|
+
const root = new Dependency('./main.ts');
|
|
492
|
+
await root.start();
|
|
596
493
|
|
|
597
|
-
|
|
598
|
-
|
|
494
|
+
console.log(root.printTree('', true, true));
|
|
495
|
+
// main (3 listeners)
|
|
496
|
+
// ├── logger (1 listeners) [shared ×2]
|
|
497
|
+
// ├── child (2 listeners)
|
|
498
|
+
// │ └── timer (1 listeners)
|
|
499
|
+
// └── parent (2 listeners)
|
|
500
|
+
// └── child (2 listeners) [shared ×2]
|
|
599
501
|
```
|
|
600
502
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
同时支持本地开发和 npm 安装的插件:
|
|
503
|
+
## 🔍 调试技巧
|
|
604
504
|
|
|
605
|
-
|
|
606
|
-
|
|
505
|
+
### 打印依赖树
|
|
506
|
+
```typescript
|
|
507
|
+
console.log(dep.printTree('', true, true));
|
|
607
508
|
```
|
|
608
509
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
510
|
+
### 监听所有事件
|
|
511
|
+
```typescript
|
|
512
|
+
const events = [
|
|
513
|
+
'before-start', 'started', 'before-mount', 'mounted',
|
|
514
|
+
'before-dispose', 'disposed', 'before-stop', 'stopped',
|
|
515
|
+
'before-reload', 'reloading', 'reloaded', 'error'
|
|
516
|
+
];
|
|
517
|
+
|
|
518
|
+
events.forEach(event => {
|
|
519
|
+
root.on(event, (dep) => {
|
|
520
|
+
console.log(`[${event}] ${dep.name}`);
|
|
521
|
+
});
|
|
522
|
+
});
|
|
615
523
|
```
|
|
616
524
|
|
|
617
|
-
###
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
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
|
|
525
|
+
### 查看引用关系
|
|
526
|
+
```typescript
|
|
527
|
+
console.log('引用者数量:', dep.refs.size);
|
|
528
|
+
console.log('引用者路径:', Array.from(dep.refs));
|
|
529
|
+
console.log('父节点:', dep.parent?.name);
|
|
530
|
+
console.log('子节点:', dep.children.map(c => c.name));
|
|
625
531
|
```
|
|
626
532
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
#### 插件包作者(发布方)
|
|
630
|
-
|
|
631
|
-
在你的插件包 README 中说明:
|
|
632
|
-
|
|
633
|
-
```markdown
|
|
634
|
-
## 使用方法
|
|
533
|
+
## ⚙️ 高级配置
|
|
635
534
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
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"
|
|
535
|
+
### 自定义路径解析
|
|
536
|
+
```typescript
|
|
537
|
+
class CustomDependency extends Dependency {
|
|
538
|
+
protected resolveFilePath(filePath: string): string {
|
|
539
|
+
// 自定义路径解析逻辑
|
|
540
|
+
return super.resolveFilePath(filePath);
|
|
654
541
|
}
|
|
655
542
|
}
|
|
656
|
-
\`\`\`
|
|
657
543
|
```
|
|
658
544
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
或者在启动命令中:
|
|
667
|
-
|
|
668
|
-
```json
|
|
669
|
-
{
|
|
670
|
-
"scripts": {
|
|
671
|
-
"start": "DEPENDENCY_TREE_INCLUDE=node_modules/@my-org/my-plugin tsx src/index.ts"
|
|
545
|
+
### 自定义模块初始化
|
|
546
|
+
```typescript
|
|
547
|
+
class CustomDependency extends Dependency {
|
|
548
|
+
async init() {
|
|
549
|
+
// 自定义初始化逻辑
|
|
550
|
+
await super.init();
|
|
551
|
+
// 额外处理
|
|
672
552
|
}
|
|
673
553
|
}
|
|
674
554
|
```
|
|
675
555
|
|
|
676
|
-
##
|
|
677
|
-
|
|
678
|
-
### `Dependency` 类
|
|
679
|
-
|
|
680
|
-
#### 构造函数
|
|
681
|
-
|
|
682
|
-
```typescript
|
|
683
|
-
new Dependency(filePath: string)
|
|
684
|
-
```
|
|
685
|
-
|
|
686
|
-
#### 方法
|
|
556
|
+
## 📝 注意事项
|
|
687
557
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
- `printTree(prefix?, showListeners?, showPaths?)` - 打印树结构
|
|
694
|
-
- `toJSON()` - 导出为 JSON
|
|
695
|
-
- `dispatch(event, ...args)` - 触发当前节点的事件
|
|
696
|
-
- `broadcast(event, ...args)` - 广播事件到整个子树
|
|
558
|
+
1. **循环依赖**: 自动处理,通过全局池去重
|
|
559
|
+
2. **内存泄漏**: `stop()` 时自动清理缓存和副作用
|
|
560
|
+
3. **热重载**: 根节点重载保持引用,非根节点创建新实例
|
|
561
|
+
4. **共享依赖**: 通过 `refs.size` 追踪,引用计数归零时才停止
|
|
562
|
+
5. **Symbol children**: 通过文件路径间接引用,避免实例循环引用
|
|
697
563
|
|
|
698
|
-
|
|
564
|
+
## 📄 License
|
|
699
565
|
|
|
700
|
-
|
|
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` - 发生错误时
|
|
566
|
+
MIT
|
|
762
567
|
|
|
763
568
|
## 🤝 贡献
|
|
764
569
|
|
|
765
|
-
欢迎提交 Issue 和
|
|
766
|
-
|
|
767
|
-
## 📄 许可证
|
|
768
|
-
|
|
769
|
-
MIT
|
|
570
|
+
欢迎提交 Issue 和 PR!
|