mm_machine 2.9.3 → 2.9.4
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/drive.js +668 -490
- package/index.js +1141 -921
- package/mod.js +742 -711
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -1,678 +1,1220 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Manager 管理器类
|
|
3
|
+
* 负责模块注册、检索、执行,支持注册表协同和多种执行模式
|
|
4
|
+
* @class Manager
|
|
5
|
+
* @augments Drive
|
|
6
|
+
*/
|
|
7
|
+
var path = require('path');
|
|
8
|
+
var { Drive } = require('./drive');
|
|
9
|
+
var { Registry } = require('./registry');
|
|
3
10
|
|
|
4
11
|
/**
|
|
5
|
-
*
|
|
12
|
+
* 生命周期状态优先级
|
|
13
|
+
* 用于 reload 时状态恢复
|
|
6
14
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*/
|
|
17
|
-
name: 'manager',
|
|
18
|
-
/**
|
|
19
|
-
* 管理器标题
|
|
20
|
-
* @type {string}
|
|
21
|
-
*/
|
|
22
|
-
title: '管理器',
|
|
23
|
-
/**
|
|
24
|
-
* 管理器描述
|
|
25
|
-
* @type {string}
|
|
26
|
-
*/
|
|
27
|
-
description: '用于管理驱动',
|
|
28
|
-
/**
|
|
29
|
-
* 检索文件名
|
|
30
|
-
* @type {string}
|
|
31
|
-
*/
|
|
32
|
-
filename: 'manager.json',
|
|
33
|
-
/**
|
|
34
|
-
* 模板目录
|
|
35
|
-
* @type {string}
|
|
36
|
-
*/
|
|
37
|
-
tpl_dir: __dirname,
|
|
38
|
-
/**
|
|
39
|
-
* 基础目录,加载模块内置资源
|
|
40
|
-
* @type {string}
|
|
41
|
-
*/
|
|
42
|
-
base_dir: '', // '../common/manager'.fullname(__dirname),
|
|
43
|
-
/**
|
|
44
|
-
* 自定义目录,加载项目自定义资源
|
|
45
|
-
* @type {string}
|
|
46
|
-
*/
|
|
47
|
-
dir: '', // './ai/manager'.fullname(),
|
|
48
|
-
/**
|
|
49
|
-
* 搜索模式 dir按目录搜索 | file按文件名搜索
|
|
50
|
-
* @type {string}
|
|
51
|
-
*/
|
|
52
|
-
search_way: 'file',
|
|
53
|
-
/**
|
|
54
|
-
* 类型 script脚本 | json配置 | drive驱动
|
|
55
|
-
* @type {string}
|
|
56
|
-
*/
|
|
57
|
-
mod_type: 'drive',
|
|
58
|
-
/**
|
|
59
|
-
* 是否懒加载
|
|
60
|
-
* @type {boolean}
|
|
61
|
-
*/
|
|
62
|
-
lazy_load: true,
|
|
63
|
-
/**
|
|
64
|
-
* 模式
|
|
65
|
-
* 1.生产模式,改变文件不会重新加载
|
|
66
|
-
* 2.热更新模式,改变配置文件会重新加载配置,不重新加载脚本
|
|
67
|
-
* 3.热重载模式,改变配置文件都会加载配置和脚本
|
|
68
|
-
* 4.热更新+重载模式,改变配置文件重新加载配置和脚本,执行完后重新加载脚本
|
|
69
|
-
* 5.重载模式,执行完后重新加载脚本,避免变量污染
|
|
70
|
-
* @type {number}
|
|
71
|
-
*/
|
|
72
|
-
mode: 3,
|
|
73
|
-
/**
|
|
74
|
-
* 排序项
|
|
75
|
-
* @type {string}
|
|
76
|
-
*/
|
|
77
|
-
sort_key: 'sort',
|
|
78
|
-
/**
|
|
79
|
-
* 作用域
|
|
80
|
-
* @type {string}
|
|
81
|
-
*/
|
|
82
|
-
scope: $.val && $.val.scope ? $.val.scope + '' : 'server',
|
|
83
|
-
/**
|
|
84
|
-
* 是否启用快照加速
|
|
85
|
-
* @type {boolean}
|
|
86
|
-
*/
|
|
87
|
-
use_snapshot: true,
|
|
88
|
-
/**
|
|
89
|
-
* 快照文件路径,留空则自动生成到 cache/snapshot/ 目录下
|
|
90
|
-
* @type {string}
|
|
91
|
-
*/
|
|
92
|
-
snapshot_file: ''
|
|
93
|
-
};
|
|
15
|
+
var statePriority = {
|
|
16
|
+
created: 0,
|
|
17
|
+
loaded: 1,
|
|
18
|
+
inited: 2,
|
|
19
|
+
started: 3,
|
|
20
|
+
paused: 4,
|
|
21
|
+
ended: 5,
|
|
22
|
+
destroyed: 6
|
|
23
|
+
};
|
|
94
24
|
|
|
25
|
+
/**
|
|
26
|
+
* 静态配置默认值
|
|
27
|
+
*/
|
|
28
|
+
var defaultConfig = {
|
|
29
|
+
name: 'manager',
|
|
30
|
+
title: '管理器',
|
|
31
|
+
description: '模块管理器',
|
|
32
|
+
dir: '',
|
|
33
|
+
use_registry: false,
|
|
34
|
+
registry_file: 'cache/registry.json',
|
|
35
|
+
lazy_load: 'auto',
|
|
36
|
+
exec_mode: 1,
|
|
37
|
+
sort: 100
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
class Manager extends Drive {
|
|
95
41
|
/**
|
|
96
42
|
* 构造函数
|
|
97
|
-
* @param {object}
|
|
98
|
-
* @param {
|
|
99
|
-
* @param {
|
|
100
|
-
* @param {
|
|
43
|
+
* @param {object} cfg 配置参数
|
|
44
|
+
* @param {string} [cfg.dir] 模块存放目录
|
|
45
|
+
* @param {boolean} [cfg.use_registry=false] 是否启用注册表
|
|
46
|
+
* @param {string} [cfg.registry_file='cache/registry.json'] 注册表文件路径
|
|
47
|
+
* @param {string|boolean} [cfg.lazy_load='auto'] 全局懒加载策略
|
|
48
|
+
* @param {number} [cfg.exec_mode=1] 事件执行模式
|
|
49
|
+
* @param parent
|
|
50
|
+
* @param mods
|
|
51
|
+
* @param DriveClass
|
|
101
52
|
*/
|
|
102
|
-
constructor(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
53
|
+
constructor(cfg, parent, mods, DriveClass) {
|
|
54
|
+
// 兼容旧签名:new Manager(config, parent, mods, Drive)
|
|
55
|
+
if (parent !== undefined || mods !== undefined) {
|
|
56
|
+
var config = { ...defaultConfig, ...cfg };
|
|
57
|
+
// 合并旧 config 中的静态配置
|
|
58
|
+
if (Manager.config) {
|
|
59
|
+
config = { ...Manager.config, ...config };
|
|
60
|
+
}
|
|
61
|
+
super(config, parent);
|
|
62
|
+
this._mods = mods || {};
|
|
63
|
+
this._infos = [];
|
|
64
|
+
this.registry = null;
|
|
65
|
+
this.dir = config.dir || '';
|
|
66
|
+
if (DriveClass) {
|
|
67
|
+
this.Drive = DriveClass;
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
var config = { ...defaultConfig, ...cfg };
|
|
71
|
+
super(config);
|
|
72
|
+
this._mods = {};
|
|
73
|
+
this._infos = [];
|
|
74
|
+
this.registry = null;
|
|
75
|
+
this.dir = config.dir || '';
|
|
117
76
|
}
|
|
118
77
|
}
|
|
119
|
-
|
|
120
|
-
get list() {
|
|
121
|
-
return this.getList();
|
|
122
|
-
}
|
|
123
78
|
}
|
|
124
79
|
|
|
80
|
+
// ==================== 注册表管理 ====================
|
|
81
|
+
|
|
125
82
|
/**
|
|
126
|
-
*
|
|
127
|
-
* @
|
|
83
|
+
* 关联注册表实例
|
|
84
|
+
* @param {object} registry Registry 实例
|
|
128
85
|
*/
|
|
129
|
-
Manager.prototype.
|
|
130
|
-
|
|
86
|
+
Manager.prototype.setRegistry = function (registry) {
|
|
87
|
+
this.registry = registry;
|
|
131
88
|
};
|
|
132
89
|
|
|
133
90
|
/**
|
|
134
|
-
*
|
|
135
|
-
* @returns {object
|
|
91
|
+
* 获取注册表实例
|
|
92
|
+
* @returns {object|null} Registry 实例
|
|
136
93
|
*/
|
|
137
|
-
Manager.prototype.
|
|
138
|
-
return this.
|
|
94
|
+
Manager.prototype.getRegistry = function () {
|
|
95
|
+
return this.registry;
|
|
139
96
|
};
|
|
140
97
|
|
|
98
|
+
// ==================== 模块查询 ====================
|
|
99
|
+
|
|
141
100
|
/**
|
|
142
|
-
*
|
|
143
|
-
* @param {string} name
|
|
144
|
-
* @
|
|
145
|
-
* @param {boolean} state 状态
|
|
146
|
-
* @param {string} file 文件名
|
|
147
|
-
* @returns {object} 模块状态模型
|
|
101
|
+
* 获取模块
|
|
102
|
+
* @param {string} name 模块名称
|
|
103
|
+
* @returns {object|null} 模块实例,不存在返回 null
|
|
148
104
|
*/
|
|
149
|
-
Manager.prototype.
|
|
150
|
-
return
|
|
151
|
-
name,
|
|
152
|
-
sort,
|
|
153
|
-
state,
|
|
154
|
-
enabled: true,
|
|
155
|
-
file: file || '',
|
|
156
|
-
get available() {
|
|
157
|
-
return this.state == true && this.enabled;
|
|
158
|
-
}
|
|
159
|
-
};
|
|
105
|
+
Manager.prototype.getMod = function (name) {
|
|
106
|
+
return this._mods[name];
|
|
160
107
|
};
|
|
161
108
|
|
|
162
109
|
/**
|
|
163
|
-
*
|
|
164
|
-
* @
|
|
110
|
+
* 获取所有模块
|
|
111
|
+
* @returns {object} 模块对象 { name: Drive }
|
|
165
112
|
*/
|
|
166
|
-
Manager.prototype.
|
|
167
|
-
|
|
168
|
-
for (let i = 0; i < this.infos.length; i++) {
|
|
169
|
-
let o = this.infos[i];
|
|
170
|
-
if (o.name == info.name) {
|
|
171
|
-
this.infos[i] = info;
|
|
172
|
-
has = true;
|
|
173
|
-
break;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
if (!has) {
|
|
177
|
-
this.infos.push(info);
|
|
178
|
-
}
|
|
113
|
+
Manager.prototype.getMods = function () {
|
|
114
|
+
return this._mods;
|
|
179
115
|
};
|
|
180
116
|
|
|
181
117
|
/**
|
|
182
|
-
*
|
|
183
|
-
* @param {string} keyword
|
|
184
|
-
* @returns {object
|
|
118
|
+
* 获取模块列表
|
|
119
|
+
* @param {string} [keyword] 模糊匹配关键词,匹配 name 和 title
|
|
120
|
+
* @returns {Array<object>} 模块实例数组
|
|
185
121
|
*/
|
|
186
122
|
Manager.prototype.getList = function (keyword) {
|
|
187
|
-
|
|
188
|
-
for (
|
|
189
|
-
|
|
190
|
-
|
|
123
|
+
var list = [];
|
|
124
|
+
for (var i = 0; i < this._infos.length; i++) {
|
|
125
|
+
var info = this._infos[i];
|
|
126
|
+
var mod = this.getMod(info.name);
|
|
191
127
|
if (!mod) {
|
|
192
128
|
continue;
|
|
193
129
|
}
|
|
130
|
+
// 合并状态信息
|
|
194
131
|
mod.config.state = info.state;
|
|
195
132
|
list.push(mod);
|
|
196
133
|
}
|
|
197
134
|
if (keyword) {
|
|
198
|
-
list = list.filter((o) =>
|
|
135
|
+
list = list.filter((o) => {
|
|
136
|
+
return (o.config.name && o.config.name.indexOf(keyword) !== -1) ||
|
|
137
|
+
(o.config.title && o.config.title.indexOf(keyword) !== -1) ||
|
|
138
|
+
(o.config.description && o.config.description.indexOf(keyword) !== -1);
|
|
139
|
+
});
|
|
199
140
|
}
|
|
200
141
|
return list;
|
|
201
142
|
};
|
|
202
143
|
|
|
203
144
|
/**
|
|
204
|
-
*
|
|
205
|
-
* @returns {
|
|
145
|
+
* 获取模块信息列表
|
|
146
|
+
* @returns {Array<object>} 模块信息数组
|
|
206
147
|
*/
|
|
207
|
-
Manager.prototype.
|
|
208
|
-
return this.
|
|
148
|
+
Manager.prototype.getInfos = function () {
|
|
149
|
+
return this._infos;
|
|
209
150
|
};
|
|
210
151
|
|
|
211
152
|
/**
|
|
212
|
-
*
|
|
213
|
-
* @
|
|
153
|
+
* 设置模块
|
|
154
|
+
* @param {string} name 模块名称
|
|
155
|
+
* @param {object} mod 模块实例
|
|
156
|
+
* @returns {object|null} 设置的模块实例
|
|
214
157
|
*/
|
|
215
|
-
Manager.prototype.
|
|
216
|
-
|
|
158
|
+
Manager.prototype.setMod = function (name, mod) {
|
|
159
|
+
this._mods[name] = mod;
|
|
160
|
+
return this.getMod(name);
|
|
217
161
|
};
|
|
218
162
|
|
|
219
163
|
/**
|
|
220
|
-
*
|
|
164
|
+
* 更新模块信息
|
|
165
|
+
* 部分合并,只覆盖 info 中存在的字段
|
|
166
|
+
* @param {string} name 模块名称
|
|
167
|
+
* @param {object} info 要更新的信息字段
|
|
221
168
|
*/
|
|
222
|
-
Manager.prototype.
|
|
169
|
+
Manager.prototype.setInfo = function (name, info) {
|
|
170
|
+
// 兼容旧签名:setInfo({ name, sort, state, ... })
|
|
171
|
+
var info_name = name;
|
|
172
|
+
var info_data = info;
|
|
173
|
+
if (typeof name === 'object' && name !== null) {
|
|
174
|
+
info_data = name;
|
|
175
|
+
info_name = info_data.name;
|
|
176
|
+
}
|
|
177
|
+
if (!info_data || !info_name) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
for (var i = 0; i < this._infos.length; i++) {
|
|
181
|
+
if (this._infos[i].name === info_name) {
|
|
182
|
+
for (var k in info_data) {
|
|
183
|
+
this._infos[i][k] = info_data[k];
|
|
184
|
+
}
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// 不存在则追加
|
|
189
|
+
this._infos.push(info_data);
|
|
190
|
+
};
|
|
223
191
|
|
|
224
192
|
/**
|
|
225
|
-
*
|
|
193
|
+
* 删除模块(仅内存,不删除文件)
|
|
194
|
+
* @param {string} name 模块名称
|
|
195
|
+
* @returns {object|null} 删除的模块实例
|
|
226
196
|
*/
|
|
227
|
-
Manager.prototype.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
let o = mods[name];
|
|
232
|
-
let info = this.newInfo(name, o.config.sort, o.config.state, o.config_file);
|
|
233
|
-
this.infos.push(info);
|
|
197
|
+
Manager.prototype.delMod = function (name) {
|
|
198
|
+
var mod = this._mods[name];
|
|
199
|
+
if (mod) {
|
|
200
|
+
delete this._mods[name];
|
|
234
201
|
}
|
|
235
|
-
|
|
202
|
+
return mod;
|
|
236
203
|
};
|
|
237
204
|
|
|
238
205
|
/**
|
|
239
|
-
*
|
|
206
|
+
* 清除所有模块
|
|
207
|
+
* 先遍历调用所有模块的 end() 和 destroy() 清理,再清空内存
|
|
240
208
|
*/
|
|
241
|
-
Manager.prototype.
|
|
242
|
-
var
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
209
|
+
Manager.prototype.clearMods = function () {
|
|
210
|
+
for (var name in this._mods) {
|
|
211
|
+
var mod = this._mods[name];
|
|
212
|
+
try {
|
|
213
|
+
if (mod.lifecycle_state === 'started' || mod.lifecycle_state === 'paused') {
|
|
214
|
+
mod.end();
|
|
215
|
+
}
|
|
216
|
+
if (mod.lifecycle_state === 'ended') {
|
|
217
|
+
mod.destroy();
|
|
218
|
+
}
|
|
219
|
+
} catch {
|
|
220
|
+
// 清理异常不影响其他模块
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
this._mods = {};
|
|
224
|
+
this._infos = [];
|
|
248
225
|
};
|
|
249
226
|
|
|
227
|
+
// ==================== 模块注册/创建 ====================
|
|
228
|
+
|
|
250
229
|
/**
|
|
251
|
-
*
|
|
230
|
+
* 创建并注册模块
|
|
231
|
+
* 根据 type 决定实例化类
|
|
232
|
+
* @param {string} config_file 配置文件路径
|
|
233
|
+
* @param {object} [cfg] 配置对象,不传则从文件读取
|
|
234
|
+
* @param {object|string} [script] 脚本内容,不传则从文件读取
|
|
235
|
+
* @returns {object} 模块实例
|
|
252
236
|
*/
|
|
253
|
-
Manager.prototype.
|
|
254
|
-
|
|
237
|
+
Manager.prototype.registerMod = function (config_file, cfg, script) {
|
|
238
|
+
var config = cfg || {};
|
|
239
|
+
|
|
240
|
+
// 未提供 cfg 时从文件读取
|
|
241
|
+
if (!config.name && config_file) {
|
|
242
|
+
try {
|
|
243
|
+
var content = require(config_file);
|
|
244
|
+
config = { ...config, ...content };
|
|
245
|
+
} catch {
|
|
246
|
+
// 文件读取失败,使用传入的 cfg
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// 创建模块实例
|
|
251
|
+
var mod = this._createModInstance(config);
|
|
252
|
+
mod.config_file = config_file;
|
|
253
|
+
mod.mode = config.mode || 3;
|
|
254
|
+
|
|
255
|
+
// 注册脚本方法
|
|
256
|
+
this._regScriptMethods(mod, script);
|
|
257
|
+
|
|
258
|
+
this._mods[config.name] = mod;
|
|
259
|
+
return mod;
|
|
255
260
|
};
|
|
256
261
|
|
|
257
|
-
// ==== 模块管理接口 ====
|
|
258
262
|
/**
|
|
259
|
-
*
|
|
263
|
+
* 根据类型创建模块实例
|
|
264
|
+
* @param {object} config 配置对象
|
|
265
|
+
* @returns {object} 模块实例
|
|
266
|
+
* @private
|
|
260
267
|
*/
|
|
261
|
-
Manager.prototype.
|
|
262
|
-
|
|
263
|
-
|
|
268
|
+
Manager.prototype._createModInstance = function (config) {
|
|
269
|
+
var mod_type = config.type || 'plugin';
|
|
270
|
+
if (mod_type === 'manager') {
|
|
271
|
+
return new Manager(config);
|
|
264
272
|
}
|
|
273
|
+
return new Drive(config);
|
|
265
274
|
};
|
|
266
275
|
|
|
267
276
|
/**
|
|
268
|
-
*
|
|
269
|
-
* @param {
|
|
270
|
-
* @
|
|
277
|
+
* 注册脚本方法到模块
|
|
278
|
+
* @param {object} mod 模块实例
|
|
279
|
+
* @param {object|string} script 脚本内容
|
|
280
|
+
* @private
|
|
271
281
|
*/
|
|
272
|
-
Manager.prototype.
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
282
|
+
Manager.prototype._regScriptMethods = function (mod, script) {
|
|
283
|
+
if (!script || typeof script !== 'object') {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
for (var key in script) {
|
|
287
|
+
var val = script[key];
|
|
288
|
+
if (typeof val === 'function') {
|
|
289
|
+
mod.setMethod(key, val);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 向后兼容:infos 和 mods 通过 getter/setter 暴露
|
|
295
|
+
Object.defineProperty(Manager.prototype, 'infos', {
|
|
296
|
+
get: function () { return this._infos; },
|
|
297
|
+
set: function (val) { this._infos = val; }
|
|
298
|
+
});
|
|
299
|
+
Object.defineProperty(Manager.prototype, 'mods', {
|
|
300
|
+
get: function () { return this._mods; },
|
|
301
|
+
set: function (val) { this._mods = val; }
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* 从注册表数据直接创建模块实例(轻量版,0 IO)
|
|
306
|
+
* 已存在的模块跳过(不重复创建)
|
|
307
|
+
* @param {object} data 注册表数据
|
|
308
|
+
* @param {string} data.name 模块名称
|
|
309
|
+
* @param {string} data.config_file 配置文件路径
|
|
310
|
+
* @param {string} [data.type] 模块类型
|
|
311
|
+
* @returns {object|null} 创建的模块实例,已存在返回 null
|
|
312
|
+
*/
|
|
313
|
+
Manager.prototype.registerModEx = function (data) {
|
|
314
|
+
if (!data || !data.name) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// 去重检查
|
|
319
|
+
if (this.getMod(data.name)) {
|
|
320
|
+
return null;
|
|
276
321
|
}
|
|
322
|
+
|
|
323
|
+
// 根据 type 选择实例化类
|
|
324
|
+
var mod_type = data.type || 'plugin';
|
|
325
|
+
var mod;
|
|
326
|
+
if (mod_type === 'manager') {
|
|
327
|
+
mod = new Manager(data);
|
|
328
|
+
} else {
|
|
329
|
+
mod = new Drive(data);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
mod.config_file = data.config_file || '';
|
|
333
|
+
this._mods[data.name] = mod;
|
|
277
334
|
return mod;
|
|
278
335
|
};
|
|
279
336
|
|
|
280
337
|
/**
|
|
281
|
-
*
|
|
282
|
-
* @param {
|
|
283
|
-
* @param {
|
|
284
|
-
* @returns {object
|
|
338
|
+
* 创建文件模块并注册
|
|
339
|
+
* @param {object} cfg 模块配置
|
|
340
|
+
* @param {string} [file] 目标文件路径,不传则按规则生成
|
|
341
|
+
* @returns {object} 模块实例
|
|
285
342
|
*/
|
|
286
|
-
Manager.prototype.
|
|
287
|
-
|
|
288
|
-
|
|
343
|
+
Manager.prototype.create = function (cfg, file) {
|
|
344
|
+
if (!cfg || !cfg.name) {
|
|
345
|
+
throw new TypeError('模块配置必须包含 name 字段');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
var new_cfg = { ...cfg };
|
|
349
|
+
var config_file = file || this._resolveConfigFile(new_cfg.name);
|
|
350
|
+
new_cfg.config_file = config_file;
|
|
351
|
+
|
|
352
|
+
// 创建配置文件到磁盘
|
|
353
|
+
Drive.prototype.createConfigFile(config_file, new_cfg);
|
|
354
|
+
|
|
355
|
+
return this.registerMod(config_file, new_cfg);
|
|
289
356
|
};
|
|
290
357
|
|
|
291
358
|
/**
|
|
292
|
-
*
|
|
293
|
-
* @param {
|
|
294
|
-
* @returns {object
|
|
359
|
+
* 构建模块信息对象
|
|
360
|
+
* @param {object} mod 模块实例
|
|
361
|
+
* @returns {object} 模块信息
|
|
362
|
+
* @private
|
|
295
363
|
*/
|
|
296
|
-
|
|
297
|
-
|
|
364
|
+
// eslint-disable-next-line complexity
|
|
365
|
+
Manager.prototype._buildInfo = function (mod) {
|
|
366
|
+
var cfg = mod.config || {};
|
|
367
|
+
return {
|
|
368
|
+
name: cfg.name || '',
|
|
369
|
+
title: cfg.title || '',
|
|
370
|
+
main: cfg.main || '',
|
|
371
|
+
func_name: cfg.func_name || 'main',
|
|
372
|
+
scope: cfg.scope || 0,
|
|
373
|
+
sort: cfg.sort || 100,
|
|
374
|
+
mode: cfg.mode || 3,
|
|
375
|
+
exec_mode: cfg.exec_mode || 1,
|
|
376
|
+
dir: mod.getDir ? mod.getDir() : '',
|
|
377
|
+
config_file: mod.config_file || '',
|
|
378
|
+
script_file: mod._script || '',
|
|
379
|
+
// eslint-disable-next-line mm_eslint/variable-name
|
|
380
|
+
lifecycle_state: mod.lifecycle_state || 'created',
|
|
381
|
+
is_loaded: !!mod.is_loaded,
|
|
382
|
+
enabled: cfg.enabled !== false
|
|
383
|
+
};
|
|
298
384
|
};
|
|
299
385
|
|
|
300
386
|
/**
|
|
301
|
-
*
|
|
302
|
-
* @
|
|
387
|
+
* 解析配置文件路径
|
|
388
|
+
* @param {string} name 模块名称
|
|
389
|
+
* @returns {string} 配置文件路径
|
|
390
|
+
* @private
|
|
303
391
|
*/
|
|
304
|
-
Manager.prototype.
|
|
305
|
-
|
|
392
|
+
Manager.prototype._resolveConfigFile = function (name) {
|
|
393
|
+
var base_dir = this.dir || process.cwd();
|
|
394
|
+
return path.join(base_dir, name, name + '.json');
|
|
306
395
|
};
|
|
307
396
|
|
|
397
|
+
// ==================== 模块调用 ====================
|
|
398
|
+
|
|
308
399
|
/**
|
|
309
|
-
*
|
|
310
|
-
* @param {string}
|
|
311
|
-
* @param {
|
|
312
|
-
* @param {
|
|
313
|
-
* @returns {
|
|
314
|
-
*/
|
|
315
|
-
Manager.prototype.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
400
|
+
* 调用模块方法
|
|
401
|
+
* @param {string} name 模块名称,传 null 遍历所有已启用模块
|
|
402
|
+
* @param {string} [method] 方法名,默认 'main'
|
|
403
|
+
* @param {*} [params] 参数
|
|
404
|
+
* @returns {Promise<*|Array>} 执行结果
|
|
405
|
+
*/
|
|
406
|
+
Manager.prototype.main = async function (name, method, params) {
|
|
407
|
+
var method_name = method || 'main';
|
|
408
|
+
|
|
409
|
+
if (name) {
|
|
410
|
+
// 指定模块
|
|
411
|
+
var mod = this.getMod(name);
|
|
412
|
+
if (!mod) {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
return this._runMod(mod, method_name, params);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// 遍历所有已启用模块
|
|
419
|
+
var results = [];
|
|
420
|
+
var mods = this._getSortedMods();
|
|
421
|
+
for (var i = 0; i < mods.length; i++) {
|
|
422
|
+
var mod = mods[i];
|
|
423
|
+
var cfg = mod.config || {};
|
|
424
|
+
if (cfg.enabled === false) {
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
try {
|
|
428
|
+
var result = await this._runMod(mod, method_name, params);
|
|
429
|
+
results.push({ name: mod.config.name, result: result });
|
|
430
|
+
} catch (err) {
|
|
431
|
+
results.push({ name: mod.config.name, error: err });
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return results;
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* 运行模块方法(自动推进生命周期)
|
|
439
|
+
* @param {object} mod 模块实例
|
|
440
|
+
* @param {string} method 方法名
|
|
441
|
+
* @param {*} params 参数
|
|
442
|
+
* @returns {Promise<*>} 执行结果
|
|
443
|
+
* @private
|
|
444
|
+
*/
|
|
445
|
+
Manager.prototype._runMod = async function (mod, method, params) {
|
|
446
|
+
var state = mod.lifecycle_state;
|
|
447
|
+
|
|
448
|
+
try {
|
|
449
|
+
await this._advanceToStarted(mod, state);
|
|
450
|
+
} catch (err) {
|
|
451
|
+
// 生命周期推进失败,透传错误
|
|
452
|
+
throw err;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return mod.call(method, params);
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* 推进模块到 started 状态
|
|
460
|
+
* @param {object} mod 模块实例
|
|
461
|
+
* @param {string} state 当前状态
|
|
462
|
+
* @returns {Promise<void>}
|
|
463
|
+
* @private
|
|
464
|
+
*/
|
|
465
|
+
Manager.prototype._advanceToStarted = async function (mod, state) {
|
|
466
|
+
var mod_name = mod.config.name || '';
|
|
467
|
+
|
|
468
|
+
switch (state) {
|
|
469
|
+
case 'created':
|
|
470
|
+
await mod.load();
|
|
471
|
+
await mod.init();
|
|
472
|
+
await mod.start();
|
|
320
473
|
break;
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
mod
|
|
474
|
+
case 'loaded':
|
|
475
|
+
await mod.init();
|
|
476
|
+
await mod.start();
|
|
477
|
+
break;
|
|
478
|
+
case 'inited':
|
|
479
|
+
await mod.start();
|
|
480
|
+
break;
|
|
481
|
+
case 'started':
|
|
324
482
|
break;
|
|
483
|
+
case 'paused':
|
|
484
|
+
throw new Error('模块 [' + mod_name + '] 已暂停,无法执行');
|
|
485
|
+
case 'ended':
|
|
486
|
+
await mod.load();
|
|
487
|
+
await mod.init();
|
|
488
|
+
await mod.start();
|
|
489
|
+
break;
|
|
490
|
+
case 'destroyed':
|
|
491
|
+
throw new Error('模块 [' + mod_name + '] 已销毁,无法执行');
|
|
492
|
+
default:
|
|
493
|
+
throw new Error('模块 [' + mod_name + '] 状态异常');
|
|
325
494
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* 获取按 sort 排序的模块列表
|
|
499
|
+
* @returns {Array<object>} 排序后的模块数组
|
|
500
|
+
* @private
|
|
501
|
+
*/
|
|
502
|
+
Manager.prototype._getSortedMods = function () {
|
|
503
|
+
var list = [];
|
|
504
|
+
for (var name in this._mods) {
|
|
505
|
+
list.push(this._mods[name]);
|
|
337
506
|
}
|
|
338
|
-
|
|
507
|
+
list.sort((a, b) => {
|
|
508
|
+
var sort_a = (a.config && a.config.sort) || 100;
|
|
509
|
+
var sort_b = (b.config && b.config.sort) || 100;
|
|
510
|
+
return sort_a - sort_b;
|
|
511
|
+
});
|
|
512
|
+
return list;
|
|
339
513
|
};
|
|
340
514
|
|
|
515
|
+
// ==================== 模块注销/删除 ====================
|
|
516
|
+
|
|
341
517
|
/**
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
* @param {
|
|
345
|
-
* @
|
|
346
|
-
* @returns {object} 模块对象
|
|
518
|
+
* 注销模块
|
|
519
|
+
* 先 end() 卸载,再 delMod 删除内存
|
|
520
|
+
* @param {string} name 模块名称
|
|
521
|
+
* @returns {object|null} 注销的模块实例
|
|
347
522
|
*/
|
|
348
|
-
Manager.prototype.
|
|
349
|
-
|
|
350
|
-
|
|
523
|
+
Manager.prototype.unreMod = function (name) {
|
|
524
|
+
var mod = this.getMod(name);
|
|
525
|
+
if (!mod) {
|
|
526
|
+
return null;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
try {
|
|
530
|
+
if (mod.lifecycle_state === 'started' || mod.lifecycle_state === 'paused') {
|
|
531
|
+
mod.end();
|
|
532
|
+
}
|
|
533
|
+
} catch {
|
|
534
|
+
// 忽略
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
this.delMod(name);
|
|
538
|
+
this._removeInfo(name);
|
|
351
539
|
return mod;
|
|
352
540
|
};
|
|
353
541
|
|
|
354
542
|
/**
|
|
355
|
-
*
|
|
356
|
-
* @param {string} name
|
|
357
|
-
* @returns {Promise<any>} 加载结果
|
|
543
|
+
* 删除模块(先注销,再删除配置文件+脚本文件)
|
|
544
|
+
* @param {string} name 模块名称
|
|
358
545
|
*/
|
|
359
|
-
Manager.prototype.
|
|
360
|
-
|
|
361
|
-
if (mod) {
|
|
362
|
-
return
|
|
546
|
+
Manager.prototype.remove = function (name) {
|
|
547
|
+
var mod = this.unreMod(name);
|
|
548
|
+
if (!mod) {
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// 删除配置文件
|
|
553
|
+
try {
|
|
554
|
+
if (mod.config_file && require('fs').existsSync(mod.config_file)) {
|
|
555
|
+
require('fs').unlinkSync(mod.config_file);
|
|
556
|
+
}
|
|
557
|
+
} catch {
|
|
558
|
+
// 忽略
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// 删除脚本文件
|
|
562
|
+
try {
|
|
563
|
+
if (mod._script && require('fs').existsSync(mod._script)) {
|
|
564
|
+
require('fs').unlinkSync(mod._script);
|
|
565
|
+
}
|
|
566
|
+
} catch {
|
|
567
|
+
// 忽略
|
|
363
568
|
}
|
|
364
|
-
return null;
|
|
365
569
|
};
|
|
366
570
|
|
|
367
571
|
/**
|
|
368
|
-
*
|
|
369
|
-
* @param {string} name
|
|
370
|
-
* @
|
|
572
|
+
* 移除模块信息
|
|
573
|
+
* @param {string} name 模块名称
|
|
574
|
+
* @private
|
|
371
575
|
*/
|
|
372
|
-
Manager.prototype.
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
576
|
+
Manager.prototype._removeInfo = function (name) {
|
|
577
|
+
for (var i = 0; i < this._infos.length; i++) {
|
|
578
|
+
if (this._infos[i].name === name) {
|
|
579
|
+
this._infos.splice(i, 1);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
376
582
|
}
|
|
377
|
-
return null;
|
|
378
583
|
};
|
|
379
584
|
|
|
585
|
+
// ==================== 重载模块 ====================
|
|
586
|
+
|
|
380
587
|
/**
|
|
381
588
|
* 重载模块
|
|
382
|
-
*
|
|
383
|
-
* @
|
|
589
|
+
* 清除缓存 → 重新执行生命周期到原状态
|
|
590
|
+
* @param {string} name 模块名称
|
|
591
|
+
* @returns {Promise<object>} 重载后的模块状态
|
|
384
592
|
*/
|
|
385
|
-
Manager.prototype.
|
|
386
|
-
|
|
387
|
-
if (mod) {
|
|
388
|
-
|
|
593
|
+
Manager.prototype.reload = async function (name) {
|
|
594
|
+
var mod = this.getMod(name);
|
|
595
|
+
if (!mod) {
|
|
596
|
+
throw new Error('模块 [' + name + '] 不存在');
|
|
389
597
|
}
|
|
390
|
-
|
|
598
|
+
|
|
599
|
+
var current_state = mod.lifecycle_state;
|
|
600
|
+
var priority = statePriority[current_state] !== undefined ? statePriority[current_state] : 0;
|
|
601
|
+
|
|
602
|
+
// 清理:优先级 >= 3 时先 end()
|
|
603
|
+
if (priority >= 3) {
|
|
604
|
+
try {
|
|
605
|
+
await mod.end();
|
|
606
|
+
} catch {
|
|
607
|
+
// 忽略
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// 清除 require.cache
|
|
612
|
+
this._clearModCache(mod);
|
|
613
|
+
|
|
614
|
+
// 重新执行生命周期到原状态
|
|
615
|
+
try {
|
|
616
|
+
await this._restoreModState(mod, priority);
|
|
617
|
+
} catch (err) {
|
|
618
|
+
throw err;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
return mod.lifecycle_state;
|
|
391
622
|
};
|
|
392
623
|
|
|
393
624
|
/**
|
|
394
|
-
*
|
|
395
|
-
* @param {
|
|
396
|
-
* @
|
|
625
|
+
* 清除模块的 require 缓存
|
|
626
|
+
* @param {object} mod 模块实例
|
|
627
|
+
* @private
|
|
397
628
|
*/
|
|
398
|
-
Manager.prototype.
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
629
|
+
Manager.prototype._clearModCache = function (mod) {
|
|
630
|
+
if (!mod._script) {
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
try {
|
|
634
|
+
var resolved = require.resolve(mod._script);
|
|
635
|
+
if (require.cache[resolved]) {
|
|
636
|
+
delete require.cache[resolved];
|
|
637
|
+
}
|
|
638
|
+
} catch {
|
|
639
|
+
// 忽略
|
|
640
|
+
}
|
|
404
641
|
};
|
|
405
642
|
|
|
406
643
|
/**
|
|
407
|
-
*
|
|
408
|
-
* @param {
|
|
409
|
-
* @param {
|
|
410
|
-
* @
|
|
644
|
+
* 恢复模块到指定优先级状态
|
|
645
|
+
* @param {object} mod 模块实例
|
|
646
|
+
* @param {number} priority 目标优先级
|
|
647
|
+
* @returns {Promise<void>}
|
|
648
|
+
* @private
|
|
411
649
|
*/
|
|
412
|
-
Manager.prototype.
|
|
413
|
-
|
|
414
|
-
|
|
650
|
+
Manager.prototype._restoreModState = async function (mod, priority) {
|
|
651
|
+
if (priority >= 1) {
|
|
652
|
+
await mod.load();
|
|
653
|
+
}
|
|
654
|
+
if (priority >= 2) {
|
|
655
|
+
await mod.init();
|
|
656
|
+
}
|
|
657
|
+
if (priority >= 3) {
|
|
658
|
+
await mod.start();
|
|
659
|
+
}
|
|
660
|
+
if (priority >= 4) {
|
|
661
|
+
await mod.pause();
|
|
415
662
|
}
|
|
416
663
|
};
|
|
417
664
|
|
|
665
|
+
// ==================== 扫描注册 ====================
|
|
666
|
+
|
|
418
667
|
/**
|
|
419
|
-
*
|
|
668
|
+
* 扫描目录并注册新模块
|
|
669
|
+
* @param {object} scanner DirScanner 实例
|
|
670
|
+
* @param {string[]} dirs 要扫描的目录列表
|
|
671
|
+
* @returns {Promise<number>} 成功注册的模块数量
|
|
420
672
|
*/
|
|
421
|
-
Manager.prototype.
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
for (let name in mods) {
|
|
425
|
-
let mod = mods[name];
|
|
426
|
-
promises.push(mod.do('load'));
|
|
673
|
+
Manager.prototype.scanAndRegister = async function (scanner, dirs) {
|
|
674
|
+
if (!scanner || typeof scanner.scanAndParse !== 'function') {
|
|
675
|
+
throw new TypeError('scanner 必须包含 scanAndParse 方法');
|
|
427
676
|
}
|
|
428
|
-
|
|
677
|
+
if (!dirs || !Array.isArray(dirs)) {
|
|
678
|
+
return 0;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
var count = 0;
|
|
682
|
+
|
|
683
|
+
for (var i = 0; i < dirs.length; i++) {
|
|
684
|
+
count += this._scanDirModules(scanner, dirs[i]);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
return count;
|
|
429
688
|
};
|
|
430
689
|
|
|
431
690
|
/**
|
|
432
|
-
*
|
|
433
|
-
* @
|
|
691
|
+
* 扫描单个目录并注册模块
|
|
692
|
+
* @param {object} scanner DirScanner 实例
|
|
693
|
+
* @param {string} dir 目录路径
|
|
694
|
+
* @returns {number} 成功注册的模块数量
|
|
695
|
+
* @private
|
|
434
696
|
*/
|
|
435
|
-
Manager.prototype.
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
697
|
+
Manager.prototype._scanDirModules = function (scanner, dir) {
|
|
698
|
+
var count = 0;
|
|
699
|
+
|
|
700
|
+
try {
|
|
701
|
+
var entries = scanner.scanAndParse(dir, '*.json');
|
|
702
|
+
for (var j = 0; j < entries.length; j++) {
|
|
703
|
+
var entry = entries[j];
|
|
704
|
+
var cfg = entry.config;
|
|
705
|
+
if (cfg && cfg.name && !this.getMod(cfg.name)) {
|
|
706
|
+
this.registerMod(entry.file, cfg);
|
|
707
|
+
count++;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
} catch {
|
|
711
|
+
// 单个目录扫描失败不影响其他目录
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
return count;
|
|
442
715
|
};
|
|
443
716
|
|
|
717
|
+
// ==================== 启动引导 ====================
|
|
718
|
+
|
|
444
719
|
/**
|
|
445
|
-
*
|
|
446
|
-
*
|
|
447
|
-
* @returns {
|
|
448
|
-
*/
|
|
449
|
-
Manager.prototype.
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
720
|
+
* 启动引导
|
|
721
|
+
* 外部入口,根据 use_registry 决定走注册表路径还是旧路径
|
|
722
|
+
* @returns {Promise<void>}
|
|
723
|
+
*/
|
|
724
|
+
Manager.prototype.boot = async function () {
|
|
725
|
+
if (this.config.use_registry) {
|
|
726
|
+
await this._bootWithRegistry();
|
|
727
|
+
} else {
|
|
728
|
+
await this._bootWithoutRegistry();
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* 注册表路径启动引导
|
|
734
|
+
* @returns {Promise<void>}
|
|
735
|
+
* @private
|
|
736
|
+
*/
|
|
737
|
+
Manager.prototype._bootWithRegistry = async function () {
|
|
738
|
+
if (!this.registry) {
|
|
739
|
+
this.registry = new Registry(this, {});
|
|
740
|
+
}
|
|
741
|
+
await this.registry.boot();
|
|
742
|
+
|
|
743
|
+
// 注册表为空时,扫描文件系统
|
|
744
|
+
if (this.registry.getEntryCount() === 0 && this.dir) {
|
|
745
|
+
await this._scanAndFillRegistry();
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* 旧路径启动引导(直接扫描目录)
|
|
751
|
+
* @returns {Promise<void>}
|
|
752
|
+
* @private
|
|
753
|
+
*/
|
|
754
|
+
Manager.prototype._bootWithoutRegistry = async function () {
|
|
755
|
+
if (!this.dir) {
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
var { DirScanner } = require('./dir_scanner');
|
|
760
|
+
var scanner = new DirScanner();
|
|
761
|
+
var entries = scanner.scanAndParse(this.dir, '*.json');
|
|
762
|
+
|
|
763
|
+
for (var i = 0; i < entries.length; i++) {
|
|
764
|
+
var entry = entries[i];
|
|
765
|
+
if (entry.config && entry.config.name) {
|
|
766
|
+
this.registerMod(entry.file, entry.config);
|
|
457
767
|
}
|
|
458
768
|
}
|
|
459
|
-
|
|
769
|
+
|
|
770
|
+
// 启动自启动模块
|
|
771
|
+
await this._startAutoModules();
|
|
460
772
|
};
|
|
461
773
|
|
|
462
774
|
/**
|
|
463
|
-
*
|
|
775
|
+
* 扫描文件系统并填充注册表
|
|
776
|
+
* @returns {Promise<void>}
|
|
464
777
|
* @private
|
|
465
|
-
* @param {string} dir 目录路径
|
|
466
|
-
* @returns {Array} 文件路径数组
|
|
467
778
|
*/
|
|
468
|
-
Manager.prototype.
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
779
|
+
Manager.prototype._scanAndFillRegistry = async function () {
|
|
780
|
+
var { DirScanner } = require('./dir_scanner');
|
|
781
|
+
var scanner = new DirScanner();
|
|
782
|
+
var entries = scanner.scanAndParse(this.dir, '*.json');
|
|
783
|
+
if (entries.length > 0) {
|
|
784
|
+
this.registry.registerAll(entries);
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* 启动所有自启动模块
|
|
790
|
+
* @returns {Promise<void>}
|
|
791
|
+
* @private
|
|
792
|
+
*/
|
|
793
|
+
Manager.prototype._startAutoModules = async function () {
|
|
794
|
+
var mods = this._getSortedMods();
|
|
795
|
+
for (var j = 0; j < mods.length; j++) {
|
|
796
|
+
var mod = mods[j];
|
|
797
|
+
if (mod.config && mod.config.auto_start) {
|
|
798
|
+
try {
|
|
799
|
+
await mod.load();
|
|
800
|
+
await mod.init();
|
|
801
|
+
await mod.start();
|
|
802
|
+
} catch {
|
|
803
|
+
// 单个模块启动失败不影响其他模块
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
// ==================== 生命周期传播 ====================
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* 启动核心
|
|
813
|
+
* 先调用父类逻辑,再传播到子模块
|
|
814
|
+
* @returns {Promise<void>}
|
|
815
|
+
* @private
|
|
816
|
+
* @override
|
|
817
|
+
*/
|
|
818
|
+
Manager.prototype._startCore = async function () {
|
|
819
|
+
if (this.lifecycle_state !== 'inited') {
|
|
820
|
+
if (this.lifecycle_state === 'started') {
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
throw new Error('模块未初始化,无法启动');
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
this.emit('start:before');
|
|
827
|
+
|
|
828
|
+
try {
|
|
829
|
+
var start_hook = this.getMethod('_start');
|
|
830
|
+
if (start_hook) {
|
|
831
|
+
var result = start_hook(this);
|
|
832
|
+
if (result instanceof Promise) {
|
|
833
|
+
await result;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// 传播到子模块
|
|
838
|
+
await this._propToChildren('start');
|
|
839
|
+
|
|
840
|
+
this.lifecycle_state = 'started';
|
|
841
|
+
this.emit('start:success');
|
|
842
|
+
} catch (err) {
|
|
843
|
+
this.emit('start:error', err);
|
|
844
|
+
throw err;
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* 暂停核心
|
|
850
|
+
* 先调用父类逻辑,再传播到子模块
|
|
851
|
+
* @returns {Promise<void>}
|
|
852
|
+
* @private
|
|
853
|
+
* @override
|
|
854
|
+
*/
|
|
855
|
+
Manager.prototype._pauseCore = async function () {
|
|
856
|
+
if (this.lifecycle_state !== 'started') {
|
|
857
|
+
throw new Error('模块未启动,无法暂停');
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
this.emit('pause:before');
|
|
861
|
+
|
|
862
|
+
try {
|
|
863
|
+
var pause_hook = this.getMethod('_pause');
|
|
864
|
+
if (pause_hook) {
|
|
865
|
+
pause_hook(this);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// 传播到子模块
|
|
869
|
+
this._propToChildren('pause');
|
|
870
|
+
|
|
871
|
+
this.lifecycle_state = 'paused';
|
|
872
|
+
this.emit('pause:success');
|
|
873
|
+
} catch (err) {
|
|
874
|
+
this.emit('pause:error', err);
|
|
875
|
+
throw err;
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* 恢复核心
|
|
881
|
+
* 先调用父类逻辑,再传播到子模块
|
|
882
|
+
* @returns {Promise<void>}
|
|
883
|
+
* @private
|
|
884
|
+
* @override
|
|
885
|
+
*/
|
|
886
|
+
Manager.prototype._resumeCore = async function () {
|
|
887
|
+
if (this.lifecycle_state !== 'paused') {
|
|
888
|
+
throw new Error('模块未暂停,无法恢复');
|
|
473
889
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
890
|
+
|
|
891
|
+
this.emit('resume:before');
|
|
892
|
+
|
|
893
|
+
try {
|
|
894
|
+
var resume_hook = this.getMethod('_resume');
|
|
895
|
+
if (resume_hook) {
|
|
896
|
+
resume_hook(this);
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// 传播到子模块
|
|
900
|
+
await this._propToChildren('resume');
|
|
901
|
+
|
|
902
|
+
this.lifecycle_state = 'started';
|
|
903
|
+
this.emit('resume:success');
|
|
904
|
+
} catch (err) {
|
|
905
|
+
this.emit('resume:error', err);
|
|
906
|
+
throw err;
|
|
477
907
|
}
|
|
478
|
-
|
|
479
|
-
|
|
908
|
+
};
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* 结束核心
|
|
912
|
+
* 先调用父类逻辑,再传播到子模块
|
|
913
|
+
* @returns {Promise<void>}
|
|
914
|
+
* @private
|
|
915
|
+
* @override
|
|
916
|
+
*/
|
|
917
|
+
Manager.prototype._endCore = async function () {
|
|
918
|
+
if (this.lifecycle_state !== 'started' && this.lifecycle_state !== 'paused') {
|
|
919
|
+
throw new Error('模块未启动或暂停,无法结束');
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
this.emit('end:before');
|
|
923
|
+
|
|
924
|
+
try {
|
|
925
|
+
var end_hook = this.getMethod('_end');
|
|
926
|
+
if (end_hook) {
|
|
927
|
+
end_hook(this);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// 传播到子模块
|
|
931
|
+
this._propToChildren('end');
|
|
932
|
+
|
|
933
|
+
// 卸载脚本
|
|
934
|
+
this.unloadScript();
|
|
935
|
+
this.lifecycle_state = 'ended';
|
|
936
|
+
this.emit('end:success');
|
|
937
|
+
} catch (err) {
|
|
938
|
+
this.emit('end:error', err);
|
|
939
|
+
throw err;
|
|
940
|
+
}
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
/**
|
|
944
|
+
* 销毁核心
|
|
945
|
+
* 先调用父类逻辑,再传播到子模块
|
|
946
|
+
* @returns {Promise<void>}
|
|
947
|
+
* @private
|
|
948
|
+
* @override
|
|
949
|
+
*/
|
|
950
|
+
Manager.prototype._destroyCore = async function () {
|
|
951
|
+
if (this.lifecycle_state !== 'ended') {
|
|
952
|
+
throw new Error('模块未结束,无法销毁');
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
this.emit('destroy:before');
|
|
956
|
+
|
|
957
|
+
try {
|
|
958
|
+
var destroy_hook = this.getMethod('_onDestroy');
|
|
959
|
+
if (destroy_hook) {
|
|
960
|
+
destroy_hook(this);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// 传播到子模块
|
|
964
|
+
this._propToChildren('destroy');
|
|
965
|
+
|
|
966
|
+
this._methods = {};
|
|
967
|
+
this.lifecycle_state = 'destroyed';
|
|
968
|
+
this.emit('destroy:success');
|
|
969
|
+
} catch (err) {
|
|
970
|
+
this.emit('destroy:error', err);
|
|
971
|
+
throw err;
|
|
972
|
+
}
|
|
973
|
+
};
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* 传播生命周期操作到子模块
|
|
977
|
+
* 最多 3 层深度,异常隔离
|
|
978
|
+
* @param {string} action 操作名称(start/pause/resume/end/destroy)
|
|
979
|
+
* @param {number} [cur_depth] 当前深度
|
|
980
|
+
* @returns {Promise<void>}
|
|
981
|
+
* @private
|
|
982
|
+
*/
|
|
983
|
+
Manager.prototype._propToChildren = async function (action, cur_depth) {
|
|
984
|
+
var depth = cur_depth || 0;
|
|
985
|
+
|
|
986
|
+
// 3 层深度限制
|
|
987
|
+
if (depth >= 3) {
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
var method_map = {
|
|
992
|
+
start: 'start',
|
|
993
|
+
pause: 'pause',
|
|
994
|
+
resume: 'resume',
|
|
995
|
+
end: 'end',
|
|
996
|
+
destroy: 'destroy'
|
|
997
|
+
};
|
|
998
|
+
|
|
999
|
+
var method_name = method_map[action];
|
|
1000
|
+
if (!method_name) {
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
for (var name in this._mods) {
|
|
1005
|
+
var mod = this._mods[name];
|
|
1006
|
+
if (typeof mod[method_name] !== 'function') {
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
try {
|
|
1011
|
+
await mod[method_name]();
|
|
1012
|
+
} catch {
|
|
1013
|
+
// 单个模块异常不影响其他模块
|
|
1014
|
+
}
|
|
480
1015
|
}
|
|
481
|
-
// 检索目录下所有文件
|
|
482
|
-
return await $.file.getAllAsync(dir, this.config.filename);
|
|
483
1016
|
};
|
|
484
1017
|
|
|
1018
|
+
// ==================== 基础API兼容(旧接口) ====================
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* 默认驱动类
|
|
1022
|
+
*/
|
|
1023
|
+
Manager.prototype.Drive = Drive;
|
|
1024
|
+
|
|
485
1025
|
/**
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
* @
|
|
1026
|
+
* 创建模块信息对象
|
|
1027
|
+
* 兼容旧接口 newInfo(name, sort, state, file)
|
|
1028
|
+
* @param {string} name 模块名
|
|
1029
|
+
* @param {number} sort 排序值
|
|
1030
|
+
* @param {boolean} state 状态
|
|
1031
|
+
* @param {string} [file] 文件路径
|
|
1032
|
+
* @returns {object} 模块信息对象
|
|
489
1033
|
*/
|
|
490
|
-
Manager.prototype.
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
1034
|
+
Manager.prototype.newInfo = function (name, sort, state, file) {
|
|
1035
|
+
return {
|
|
1036
|
+
name: name,
|
|
1037
|
+
sort: sort,
|
|
1038
|
+
state: state,
|
|
1039
|
+
enabled: true,
|
|
1040
|
+
file: file || '',
|
|
1041
|
+
get available() {
|
|
1042
|
+
return this.state == true && this.enabled;
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
};
|
|
1046
|
+
|
|
1047
|
+
/**
|
|
1048
|
+
* 更新模块信息列表
|
|
1049
|
+
* 从 _mods 重新构建 _infos
|
|
1050
|
+
*/
|
|
1051
|
+
Manager.prototype.updateInfo = function () {
|
|
1052
|
+
this._infos = [];
|
|
1053
|
+
for (var name in this._mods) {
|
|
1054
|
+
var o = this._mods[name];
|
|
1055
|
+
var info = this.newInfo(name, o.config.sort, o.config.state, o.config_file);
|
|
1056
|
+
this._infos.push(info);
|
|
498
1057
|
}
|
|
499
|
-
|
|
1058
|
+
this._sort();
|
|
1059
|
+
};
|
|
1060
|
+
|
|
1061
|
+
/**
|
|
1062
|
+
* 内部排序
|
|
1063
|
+
* 按 sort_key 排序 _infos
|
|
1064
|
+
* @private
|
|
1065
|
+
*/
|
|
1066
|
+
Manager.prototype._sort = function () {
|
|
1067
|
+
var sort_key = this.config.sort_key || 'sort';
|
|
1068
|
+
this._infos.sort((o1, o2) => {
|
|
1069
|
+
var p1 = o1[sort_key] || 100;
|
|
1070
|
+
var p2 = o2[sort_key] || 100;
|
|
1071
|
+
return p1 - p2;
|
|
1072
|
+
});
|
|
1073
|
+
};
|
|
1074
|
+
|
|
1075
|
+
/**
|
|
1076
|
+
* 排序
|
|
1077
|
+
*/
|
|
1078
|
+
Manager.prototype.sort = function () {
|
|
1079
|
+
this._sort();
|
|
1080
|
+
};
|
|
1081
|
+
|
|
1082
|
+
/**
|
|
1083
|
+
* 获取基础目录
|
|
1084
|
+
* @returns {string} 基础目录
|
|
1085
|
+
*/
|
|
1086
|
+
Manager.prototype.getBaseDir = function () {
|
|
1087
|
+
return this.config.base_dir || '';
|
|
1088
|
+
};
|
|
1089
|
+
|
|
1090
|
+
/**
|
|
1091
|
+
* 获取模板目录
|
|
1092
|
+
* @returns {string} 模板目录
|
|
1093
|
+
*/
|
|
1094
|
+
Manager.prototype.getTplDir = function () {
|
|
1095
|
+
return this.config.tpl_dir || '';
|
|
1096
|
+
};
|
|
1097
|
+
|
|
1098
|
+
/**
|
|
1099
|
+
* 获取目录名
|
|
1100
|
+
* 前缀为当前类名小写,后缀为配置项名称
|
|
1101
|
+
* @returns {string} 目录名
|
|
1102
|
+
*/
|
|
1103
|
+
Manager.prototype.getDirName = function () {
|
|
1104
|
+
var prefix = this.constructor.name.toLowerCase();
|
|
1105
|
+
var name = this.config.name.toLowerCase();
|
|
1106
|
+
return prefix + '_' + name;
|
|
500
1107
|
};
|
|
501
1108
|
|
|
1109
|
+
// ==================== 模块生命周期管理 ====================
|
|
1110
|
+
|
|
502
1111
|
/**
|
|
503
|
-
*
|
|
504
|
-
* @param {
|
|
1112
|
+
* 加载模块
|
|
1113
|
+
* @param {string} name 模块名
|
|
1114
|
+
* @returns {Promise<*>} 加载结果
|
|
505
1115
|
*/
|
|
506
|
-
Manager.prototype.
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
mods_map: mods_map,
|
|
511
|
-
updated_at: Date.now()
|
|
512
|
-
};
|
|
513
|
-
snapshot_path.saveJson(snapshot);
|
|
514
|
-
} catch (err) {
|
|
515
|
-
this.log('error', '保存快照失败', err);
|
|
1116
|
+
Manager.prototype.loadMod = async function (name) {
|
|
1117
|
+
var mod = this.getMod(name);
|
|
1118
|
+
if (mod) {
|
|
1119
|
+
return await mod.load();
|
|
516
1120
|
}
|
|
1121
|
+
return null;
|
|
517
1122
|
};
|
|
518
1123
|
|
|
519
1124
|
/**
|
|
520
|
-
*
|
|
521
|
-
*
|
|
1125
|
+
* 卸载模块(旧接口 unloadMod -> end)
|
|
1126
|
+
* @param {string} name 模块名
|
|
1127
|
+
* @returns {Promise<*>} 卸载结果
|
|
522
1128
|
*/
|
|
523
|
-
Manager.prototype.
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
continue;
|
|
533
|
-
}
|
|
534
|
-
var dir = this._findSnapshotDir(f);
|
|
535
|
-
if (!mods_map[dir]) {
|
|
536
|
-
mods_map[dir] = [];
|
|
1129
|
+
Manager.prototype.unloadMod = async function (name) {
|
|
1130
|
+
var mod = this.getMod(name);
|
|
1131
|
+
if (mod) {
|
|
1132
|
+
try {
|
|
1133
|
+
if (mod.lifecycle_state === 'started' || mod.lifecycle_state === 'paused') {
|
|
1134
|
+
await mod.end();
|
|
1135
|
+
}
|
|
1136
|
+
} catch {
|
|
1137
|
+
// 忽略
|
|
537
1138
|
}
|
|
538
|
-
|
|
1139
|
+
return mod;
|
|
539
1140
|
}
|
|
540
|
-
|
|
1141
|
+
return null;
|
|
541
1142
|
};
|
|
542
1143
|
|
|
543
1144
|
/**
|
|
544
|
-
*
|
|
545
|
-
* @param {string}
|
|
546
|
-
* @returns {
|
|
1145
|
+
* 重载模块(旧接口 reloadMod)
|
|
1146
|
+
* @param {string} name 模块名
|
|
1147
|
+
* @returns {Promise<*>} 重载结果
|
|
547
1148
|
*/
|
|
548
|
-
Manager.prototype.
|
|
549
|
-
var
|
|
550
|
-
if (
|
|
551
|
-
return
|
|
552
|
-
}
|
|
553
|
-
var dir = this.getDir();
|
|
554
|
-
if (dir && file.startsWith(dir)) {
|
|
555
|
-
return dir;
|
|
1149
|
+
Manager.prototype.reloadMod = async function (name) {
|
|
1150
|
+
var mod = this.getMod(name);
|
|
1151
|
+
if (mod) {
|
|
1152
|
+
return await mod.reload();
|
|
556
1153
|
}
|
|
557
|
-
return
|
|
1154
|
+
return null;
|
|
558
1155
|
};
|
|
559
1156
|
|
|
560
1157
|
/**
|
|
561
|
-
*
|
|
562
|
-
*
|
|
1158
|
+
* 注销模块
|
|
1159
|
+
* 兼容旧接口 unregisterMod
|
|
1160
|
+
* @param {string} name 模块名称
|
|
1161
|
+
* @returns {object|null} 注销的模块实例
|
|
563
1162
|
*/
|
|
564
|
-
Manager.prototype.
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
if (!snapshot || !snapshot.mods_map) {
|
|
572
|
-
return null;
|
|
573
|
-
}
|
|
574
|
-
return snapshot;
|
|
575
|
-
} catch (err) {
|
|
576
|
-
this.log('error', '加载快照失败', err);
|
|
577
|
-
return null;
|
|
578
|
-
}
|
|
1163
|
+
Manager.prototype.unregisterMod = function (name) {
|
|
1164
|
+
// 先卸载
|
|
1165
|
+
this.unloadMod(name);
|
|
1166
|
+
// 再删除
|
|
1167
|
+
var mod = this.delMod(name);
|
|
1168
|
+
this._removeInfo(name);
|
|
1169
|
+
return mod;
|
|
579
1170
|
};
|
|
580
1171
|
|
|
581
1172
|
/**
|
|
582
|
-
*
|
|
583
|
-
*
|
|
584
|
-
* @param {object}
|
|
585
|
-
* @param {
|
|
1173
|
+
* 批量注册模块
|
|
1174
|
+
* @param {string[]} files 配置文件路径数组
|
|
1175
|
+
* @param {object} [cfg] 配置对象
|
|
1176
|
+
* @param {object} [script] 脚本对象
|
|
586
1177
|
*/
|
|
587
|
-
Manager.prototype.
|
|
588
|
-
if (
|
|
589
|
-
|
|
1178
|
+
Manager.prototype.registerMods = function (files, cfg, script) {
|
|
1179
|
+
if (!files || !Array.isArray(files)) {
|
|
1180
|
+
return;
|
|
590
1181
|
}
|
|
591
|
-
for (var
|
|
592
|
-
|
|
593
|
-
if (!entries || entries.length === 0) {
|
|
594
|
-
continue;
|
|
595
|
-
}
|
|
596
|
-
for (var i = 0; i < entries.length; i++) {
|
|
597
|
-
var entry = entries[i];
|
|
598
|
-
var f = entry.file;
|
|
599
|
-
// 跳过已不存在的文件,等待后台同步处理
|
|
600
|
-
if (!f.hasFile || !f.hasFile()) {
|
|
601
|
-
continue;
|
|
602
|
-
}
|
|
603
|
-
this._newModFromSnap(f, entry.config);
|
|
604
|
-
}
|
|
1182
|
+
for (var i = 0; i < files.length; i++) {
|
|
1183
|
+
this.registerMod(files[i], cfg, script);
|
|
605
1184
|
}
|
|
606
|
-
this.updateInfo();
|
|
607
|
-
this.is_loaded = true;
|
|
608
1185
|
};
|
|
609
1186
|
|
|
610
1187
|
/**
|
|
611
|
-
*
|
|
612
|
-
* @param {string} file 配置文件路径
|
|
613
|
-
* @param {object} config 模块配置
|
|
614
|
-
* @returns {object} 模块对象
|
|
1188
|
+
* 加载所有模块的脚本
|
|
615
1189
|
*/
|
|
616
|
-
Manager.prototype.
|
|
617
|
-
var
|
|
618
|
-
|
|
619
|
-
mod =
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
mod.mode = this.config.mode;
|
|
624
|
-
mod.config_file = file;
|
|
625
|
-
if (mod._preset) {
|
|
626
|
-
mod._preset();
|
|
1190
|
+
Manager.prototype.loadScripts = async function () {
|
|
1191
|
+
var promises = [];
|
|
1192
|
+
for (var name in this._mods) {
|
|
1193
|
+
var mod = this._mods[name];
|
|
1194
|
+
if (typeof mod.loadScript === 'function') {
|
|
1195
|
+
promises.push(mod.loadScript());
|
|
1196
|
+
}
|
|
627
1197
|
}
|
|
628
|
-
|
|
1198
|
+
await Promise.all(promises);
|
|
629
1199
|
};
|
|
630
1200
|
|
|
631
1201
|
/**
|
|
632
|
-
*
|
|
633
|
-
* 使用锁防止多次重入冲突
|
|
634
|
-
* @param {object} snapshot 快照数据
|
|
1202
|
+
* 加载所有模块
|
|
635
1203
|
*/
|
|
636
|
-
Manager.prototype.
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
this._syncing = true;
|
|
642
|
-
try {
|
|
643
|
-
var old_dirs = snapshot.mods_map || {};
|
|
644
|
-
var old = this._buildSet(old_dirs);
|
|
645
|
-
|
|
646
|
-
// 扫描目录,对所有文件重新注册(含快照已有模块,补全热更新监听和脚本加载)
|
|
647
|
-
for (var dir in old_dirs) {
|
|
648
|
-
var current = await this._findFiles(dir);
|
|
649
|
-
for (var fi = 0; fi < current.length; fi++) {
|
|
650
|
-
var f = current[fi];
|
|
651
|
-
this.registerMod(f);
|
|
652
|
-
old[f] = false;
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
// 移除已删除的模块
|
|
657
|
-
await this._removeStale(old);
|
|
658
|
-
// 加载所有模块脚本(lazy_load时不阻塞但后台仍加载)
|
|
659
|
-
if (!this.config.lazy_load) {
|
|
660
|
-
await this.loadScripts();
|
|
661
|
-
}
|
|
662
|
-
this.updateInfo();
|
|
663
|
-
await this._saveSnapshotDirs(Object.keys(old_dirs));
|
|
664
|
-
// this.log('info', '快照同步完成');
|
|
665
|
-
} catch (err) {
|
|
666
|
-
this.log('error', '快照增量同步失败', err);
|
|
667
|
-
} finally {
|
|
668
|
-
this._syncing = false;
|
|
1204
|
+
Manager.prototype.loadMods = async function () {
|
|
1205
|
+
var promises = [];
|
|
1206
|
+
for (var name in this._mods) {
|
|
1207
|
+
var mod = this._mods[name];
|
|
1208
|
+
promises.push(mod.load());
|
|
669
1209
|
}
|
|
1210
|
+
await Promise.all(promises);
|
|
670
1211
|
};
|
|
671
1212
|
|
|
672
1213
|
/**
|
|
673
1214
|
* 构建文件集合
|
|
674
|
-
* @param {object} dirs 目录到模块条目的映射
|
|
1215
|
+
* @param {object} dirs 目录到模块条目的映射
|
|
675
1216
|
* @returns {object} 文件集合 { file: true }
|
|
1217
|
+
* @private
|
|
676
1218
|
*/
|
|
677
1219
|
Manager.prototype._buildSet = function (dirs) {
|
|
678
1220
|
var set = {};
|
|
@@ -686,30 +1228,14 @@ Manager.prototype._buildSet = function (dirs) {
|
|
|
686
1228
|
};
|
|
687
1229
|
|
|
688
1230
|
/**
|
|
689
|
-
*
|
|
690
|
-
* @
|
|
691
|
-
|
|
692
|
-
Manager.prototype._removeStale = async function (old) {
|
|
693
|
-
for (var file in old) {
|
|
694
|
-
if (old[file] === true) {
|
|
695
|
-
var mod = this.getMod(file);
|
|
696
|
-
if (mod) {
|
|
697
|
-
await this.unloadMod(mod.config.name);
|
|
698
|
-
this.delMod(mod.config.name);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
};
|
|
703
|
-
|
|
704
|
-
/**
|
|
705
|
-
* 构建文件到config的映射
|
|
706
|
-
* @returns {object} 文件到config的映射 { file_path: config }
|
|
1231
|
+
* 构建配置映射
|
|
1232
|
+
* @returns {object} 文件到配置的映射
|
|
1233
|
+
* @private
|
|
707
1234
|
*/
|
|
708
1235
|
Manager.prototype._buildConfigMap = function () {
|
|
709
1236
|
var map = {};
|
|
710
|
-
var
|
|
711
|
-
|
|
712
|
-
var mod = mods[name];
|
|
1237
|
+
for (var name in this._mods) {
|
|
1238
|
+
var mod = this._mods[name];
|
|
713
1239
|
if (mod.config_file) {
|
|
714
1240
|
map[mod.config_file] = mod.config;
|
|
715
1241
|
}
|
|
@@ -720,8 +1246,9 @@ Manager.prototype._buildConfigMap = function () {
|
|
|
720
1246
|
/**
|
|
721
1247
|
* 构建模块条目列表
|
|
722
1248
|
* @param {string[]} files 文件路径数组
|
|
723
|
-
* @param {object} config_dict
|
|
724
|
-
* @returns {
|
|
1249
|
+
* @param {object} config_dict 文件到配置的映射
|
|
1250
|
+
* @returns {Array<{file: string, config: object}>} 模块条目数组
|
|
1251
|
+
* @private
|
|
725
1252
|
*/
|
|
726
1253
|
Manager.prototype._buildEntries = function (files, config_dict) {
|
|
727
1254
|
var entries = [];
|
|
@@ -736,334 +1263,129 @@ Manager.prototype._buildEntries = function (files, config_dict) {
|
|
|
736
1263
|
};
|
|
737
1264
|
|
|
738
1265
|
/**
|
|
739
|
-
*
|
|
740
|
-
* @param {string[]} dirs 目录列表
|
|
741
|
-
*/
|
|
742
|
-
Manager.prototype._saveSnapshotDirs = async function (dirs) {
|
|
743
|
-
var new_map = {};
|
|
744
|
-
var config_dict = this._buildConfigMap();
|
|
745
|
-
for (var i = 0; i < dirs.length; i++) {
|
|
746
|
-
var dir = dirs[i];
|
|
747
|
-
var files = await this._findFiles(dir);
|
|
748
|
-
new_map[dir] = this._buildEntries(files, config_dict);
|
|
749
|
-
}
|
|
750
|
-
this._saveSnapshot(new_map);
|
|
751
|
-
};
|
|
752
|
-
|
|
753
|
-
/**
|
|
754
|
-
* 加载所有模块的脚本
|
|
755
|
-
*/
|
|
756
|
-
Manager.prototype.loadScripts = async function () {
|
|
757
|
-
var promises = [];
|
|
758
|
-
let mods = this.getMods();
|
|
759
|
-
for (let name in mods) {
|
|
760
|
-
let mod = mods[name];
|
|
761
|
-
promises.push(mod.call('loadScript'));
|
|
762
|
-
}
|
|
763
|
-
await Promise.all(promises);
|
|
764
|
-
};
|
|
765
|
-
|
|
766
|
-
/**
|
|
767
|
-
* 更新配置前的钩子函数
|
|
1266
|
+
* 更新前的钩子
|
|
768
1267
|
*/
|
|
769
1268
|
Manager.prototype.updateBefore = function () {
|
|
770
|
-
//
|
|
1269
|
+
// 空实现,子类可重写
|
|
771
1270
|
};
|
|
772
1271
|
|
|
773
1272
|
/**
|
|
774
|
-
*
|
|
1273
|
+
* 更新后的钩子
|
|
775
1274
|
*/
|
|
776
1275
|
Manager.prototype.updateAfter = function () {
|
|
777
|
-
//
|
|
1276
|
+
// 空实现,子类可重写
|
|
778
1277
|
};
|
|
779
1278
|
|
|
780
1279
|
/**
|
|
781
1280
|
* 更新配置
|
|
782
|
-
* @param {string} dir
|
|
783
|
-
* @param {boolean} clear 是否清除缓存
|
|
1281
|
+
* @param {string} dir 检索的目录
|
|
1282
|
+
* @param {boolean} [clear] 是否清除缓存
|
|
784
1283
|
*/
|
|
785
|
-
Manager.prototype.update = async function (dir, clear
|
|
786
|
-
|
|
1284
|
+
Manager.prototype.update = async function (dir, clear) {
|
|
1285
|
+
var do_clear = clear !== false;
|
|
1286
|
+
if (do_clear) {
|
|
787
1287
|
this.clearMods();
|
|
788
1288
|
}
|
|
789
1289
|
|
|
790
|
-
var search_dir = dir || this.
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
// 批量注册模块
|
|
794
|
-
this.registerMods(files);
|
|
795
|
-
// 更新配置信息
|
|
796
|
-
this.updateInfo();
|
|
797
|
-
if (!this.config.lazy_load) {
|
|
798
|
-
// 批量加载脚本
|
|
799
|
-
await this.loadScripts();
|
|
800
|
-
}
|
|
801
|
-
this.is_loaded = true;
|
|
802
|
-
};
|
|
803
|
-
|
|
804
|
-
// ==== 高性能调用接口 ====
|
|
805
|
-
/**
|
|
806
|
-
* 调用模块的方法(高性能版本)
|
|
807
|
-
* @param {string} name 模块名
|
|
808
|
-
* @param {string} method 方法名
|
|
809
|
-
* @param {...any} params 参数集合
|
|
810
|
-
* @returns {Promise<any>} 执行结果
|
|
811
|
-
*/
|
|
812
|
-
Manager.prototype.main = async function (name, method, ...params) {
|
|
813
|
-
var result = null;
|
|
814
|
-
if (name) {
|
|
815
|
-
let mod = this.getMod(name);
|
|
816
|
-
if (mod && mod.config?.state === 1) {
|
|
817
|
-
result = await this._runMod(mod, method, ...params);
|
|
818
|
-
}
|
|
819
|
-
} else if (name === null) {
|
|
820
|
-
result = await this._runAllEnabled(method, ...params);
|
|
821
|
-
}
|
|
822
|
-
return result;
|
|
823
|
-
};
|
|
824
|
-
|
|
825
|
-
/**
|
|
826
|
-
* 私有方法:执行所有模块方法
|
|
827
|
-
* @param {string} method 方法名称
|
|
828
|
-
* @param {...any} params 方法参数集合
|
|
829
|
-
* @returns {Promise<any>} 执行结果
|
|
830
|
-
* @private
|
|
831
|
-
*/
|
|
832
|
-
Manager.prototype._runAllEnabled = async function (method, ...params) {
|
|
833
|
-
var result;
|
|
834
|
-
for (let i = 0; i < this.infos.length; i++) {
|
|
835
|
-
let config = this.infos[i];
|
|
836
|
-
let mod = this.getMod(config.name);
|
|
837
|
-
if (mod && mod.config?.state === 1) {
|
|
838
|
-
var ret = null;
|
|
839
|
-
try {
|
|
840
|
-
ret = await this._runMod(mod, method, ...params);
|
|
841
|
-
} catch (err) {
|
|
842
|
-
$.log.error(`执行模块方法失败: `, err);
|
|
843
|
-
}
|
|
844
|
-
if (ret !== null && ret !== undefined) {
|
|
845
|
-
result = ret;
|
|
846
|
-
// 如果结果不为空且有结束标志,则停止迭代
|
|
847
|
-
if (mod.config?.end) {
|
|
848
|
-
break;
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
return result;
|
|
854
|
-
};
|
|
855
|
-
|
|
856
|
-
/**
|
|
857
|
-
* 私有方法:执行模块方法
|
|
858
|
-
* @param {object} mod 模块对象
|
|
859
|
-
* @param {string} method 方法名称
|
|
860
|
-
* @param {...any} params 参数集合
|
|
861
|
-
* @returns {Promise<any>} 执行结果
|
|
862
|
-
* @private
|
|
863
|
-
*/
|
|
864
|
-
Manager.prototype._runMod = async function (mod, method, ...params) {
|
|
865
|
-
if (!mod || !method) return null;
|
|
866
|
-
|
|
867
|
-
// 确保模块已加载
|
|
868
|
-
if (!mod.isLoaded()) {
|
|
869
|
-
try {
|
|
870
|
-
await mod.call('loadScript');
|
|
871
|
-
} catch (err) {
|
|
872
|
-
$.log.error(`加载模块脚本失败: `, err);
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
// 执行方法
|
|
877
|
-
let ret = await mod.call(method, ...params);
|
|
878
|
-
// 根据模式决定是否重载
|
|
879
|
-
if (this.mode >= 4) {
|
|
880
|
-
mod.do('reload');
|
|
1290
|
+
var search_dir = dir || this.dir;
|
|
1291
|
+
if (!search_dir) {
|
|
1292
|
+
return;
|
|
881
1293
|
}
|
|
882
1294
|
|
|
883
|
-
|
|
884
|
-
};
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
* 初始化管理器
|
|
888
|
-
*/
|
|
889
|
-
Manager.prototype._initManager = function () { };
|
|
890
|
-
|
|
891
|
-
/**
|
|
892
|
-
* 获取公共模块目录
|
|
893
|
-
* @returns {string} 公共模块目录
|
|
894
|
-
*/
|
|
895
|
-
Manager.prototype.getBaseDir = function () {
|
|
896
|
-
return this.config.base_dir;
|
|
897
|
-
};
|
|
898
|
-
|
|
899
|
-
/**
|
|
900
|
-
* 获取二次开发模块目录
|
|
901
|
-
* @returns {string} 二次开发模块目录
|
|
902
|
-
*/
|
|
903
|
-
Manager.prototype.getDir = function () {
|
|
904
|
-
return this.config.dir || this.getDir();
|
|
905
|
-
};
|
|
906
|
-
|
|
907
|
-
/**
|
|
908
|
-
* 加载资源
|
|
909
|
-
*/
|
|
910
|
-
Manager.prototype._loadSources = async function () {
|
|
911
|
-
var base_dir = this.getBaseDir();
|
|
912
|
-
var dir = this.getDir();
|
|
1295
|
+
// 查找所有 json 文件
|
|
1296
|
+
var { DirScanner } = require('./dir_scanner');
|
|
1297
|
+
var scanner = new DirScanner();
|
|
1298
|
+
var entries = scanner.scanAndParse(search_dir, '*.json');
|
|
913
1299
|
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
// 快照存在,用config快速加载模块
|
|
919
|
-
this._loadFromSnapshot(snapshot.mods_map);
|
|
920
|
-
// this.log('info', '已从快照快速加载模块,正在后台同步文件变更...');
|
|
921
|
-
// 同步等待文件同步完成,确保模块已全部注册后再执行初始化,防止竞态条件
|
|
922
|
-
await this._syncFromFile(snapshot);
|
|
923
|
-
return;
|
|
1300
|
+
for (var i = 0; i < entries.length; i++) {
|
|
1301
|
+
var entry = entries[i];
|
|
1302
|
+
if (entry.config && entry.config.name) {
|
|
1303
|
+
this.registerMod(entry.file, entry.config);
|
|
924
1304
|
}
|
|
925
1305
|
}
|
|
926
1306
|
|
|
927
|
-
|
|
928
|
-
if (
|
|
929
|
-
await this.
|
|
930
|
-
await this.call('update', dir, false);
|
|
931
|
-
} else {
|
|
932
|
-
await this.call('update', dir, true);
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
// 首次扫描后保存快照(含config)
|
|
936
|
-
if (this.config.use_snapshot) {
|
|
937
|
-
var dirs = [];
|
|
938
|
-
if (base_dir) {
|
|
939
|
-
dirs.push(base_dir);
|
|
940
|
-
}
|
|
941
|
-
if (dir) {
|
|
942
|
-
dirs.push(dir);
|
|
943
|
-
}
|
|
944
|
-
await this._saveSnapshotDirs(dirs);
|
|
1307
|
+
this.updateInfo();
|
|
1308
|
+
if (!this.config.lazy_load) {
|
|
1309
|
+
await this.loadScripts();
|
|
945
1310
|
}
|
|
1311
|
+
this.is_loaded = true;
|
|
946
1312
|
};
|
|
947
1313
|
|
|
948
|
-
|
|
949
|
-
* 初始化资源
|
|
950
|
-
*/
|
|
951
|
-
Manager.prototype._initSources = async function () {
|
|
952
|
-
// 通过
|
|
953
|
-
};
|
|
954
|
-
|
|
955
|
-
/**
|
|
956
|
-
* 初始化公共属性
|
|
957
|
-
*/
|
|
958
|
-
Manager.prototype._initCore = async function () {
|
|
959
|
-
// 初始化管理器
|
|
960
|
-
this._initManager();
|
|
961
|
-
// 加载资源
|
|
962
|
-
await this._loadSources();
|
|
963
|
-
// 初始化AI客户端
|
|
964
|
-
await this._initSources();
|
|
965
|
-
};
|
|
1314
|
+
// ==================== 执行方法 ====================
|
|
966
1315
|
|
|
967
1316
|
/**
|
|
968
|
-
*
|
|
969
|
-
* @param {
|
|
1317
|
+
* 执行模块方法
|
|
1318
|
+
* @param {object} mod 模块对象
|
|
970
1319
|
* @param {string} method 方法名称
|
|
971
1320
|
* @param {...any} params 参数列表
|
|
972
|
-
* @returns {
|
|
1321
|
+
* @returns {Promise<*>} 执行结果
|
|
1322
|
+
* @private
|
|
973
1323
|
*/
|
|
974
|
-
Manager.prototype._exec = async function (mod, method
|
|
1324
|
+
Manager.prototype._exec = async function (mod, method) {
|
|
1325
|
+
var params = Array.prototype.slice.call(arguments, 2);
|
|
975
1326
|
if (mod[method]) {
|
|
976
|
-
return await mod[method](
|
|
1327
|
+
return await mod[method].apply(mod, params);
|
|
977
1328
|
} else {
|
|
978
|
-
throw new Error(
|
|
1329
|
+
throw new Error('方法' + method + '不存在');
|
|
979
1330
|
}
|
|
980
1331
|
};
|
|
981
1332
|
|
|
982
1333
|
/**
|
|
983
|
-
*
|
|
1334
|
+
* 执行模块方法
|
|
984
1335
|
* @param {string} name 模块名
|
|
985
1336
|
* @param {string} method 方法名称
|
|
986
1337
|
* @param {...any} params 参数列表
|
|
987
|
-
* @returns {
|
|
1338
|
+
* @returns {Promise<*>} 执行结果
|
|
988
1339
|
*/
|
|
989
|
-
Manager.prototype.exec = async function (name, method
|
|
990
|
-
|
|
1340
|
+
Manager.prototype.exec = async function (name, method) {
|
|
1341
|
+
var params = Array.prototype.slice.call(arguments, 2);
|
|
991
1342
|
try {
|
|
992
|
-
|
|
1343
|
+
var mod = this.getMod(name);
|
|
993
1344
|
if (!mod) {
|
|
994
|
-
throw new Error(
|
|
1345
|
+
throw new Error('模块' + name + '不存在');
|
|
995
1346
|
}
|
|
996
|
-
|
|
1347
|
+
return await this._exec.apply(this, [mod, method].concat(params));
|
|
997
1348
|
} catch (error) {
|
|
998
|
-
this.log('error',
|
|
999
|
-
|
|
1000
|
-
return ret;
|
|
1001
|
-
};
|
|
1002
|
-
|
|
1003
|
-
/**
|
|
1004
|
-
* 确保所有模块都已加载
|
|
1005
|
-
* @returns {Promise} 加载完成
|
|
1006
|
-
*/
|
|
1007
|
-
Manager.prototype._ensureAllLoaded = async function () {
|
|
1008
|
-
var infos = this.getInfos();
|
|
1009
|
-
var promises = [];
|
|
1010
|
-
|
|
1011
|
-
for (var i = 0; i < infos.length; i++) {
|
|
1012
|
-
var mod = this.getMod(infos[i].name);
|
|
1013
|
-
if (mod && !mod.isLoaded()) {
|
|
1014
|
-
((info) => {
|
|
1015
|
-
promises.push(
|
|
1016
|
-
mod.call('loadScript').catch((err) => {
|
|
1017
|
-
this.log('error', `模块${info.name}加载失败`, err);
|
|
1018
|
-
})
|
|
1019
|
-
);
|
|
1020
|
-
})(infos[i]);
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
if (promises.length > 0) {
|
|
1024
|
-
await Promise.all(promises);
|
|
1349
|
+
this.log('error', method + '方法执行失败!', error);
|
|
1350
|
+
return null;
|
|
1025
1351
|
}
|
|
1026
1352
|
};
|
|
1027
1353
|
|
|
1028
1354
|
/**
|
|
1029
1355
|
* 异步触发事件,按顺序执行等待完成
|
|
1030
1356
|
* @param {string} method 方法名称
|
|
1031
|
-
* @param
|
|
1032
|
-
* @returns {Promise} 事件触发结果
|
|
1357
|
+
* @param {...any} params 事件参数
|
|
1358
|
+
* @returns {Promise<*>} 事件触发结果
|
|
1033
1359
|
*/
|
|
1034
|
-
Manager.prototype.runWait = async function (method
|
|
1035
|
-
// 参数校验
|
|
1360
|
+
Manager.prototype.runWait = async function (method) {
|
|
1036
1361
|
if (typeof method !== 'string') {
|
|
1037
1362
|
throw new TypeError('方法名称必须是字符串');
|
|
1038
1363
|
}
|
|
1364
|
+
var params = Array.prototype.slice.call(arguments, 1);
|
|
1365
|
+
var result;
|
|
1366
|
+
var infos = this.getInfos();
|
|
1039
1367
|
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
for (let i = 0; i < infos.length; i++) {
|
|
1044
|
-
let info = infos[i];
|
|
1045
|
-
let mod = this.getMod(info.name);
|
|
1368
|
+
for (var i = 0; i < infos.length; i++) {
|
|
1369
|
+
var info = infos[i];
|
|
1370
|
+
var mod = this.getMod(info.name);
|
|
1046
1371
|
if (!mod) {
|
|
1047
|
-
continue;
|
|
1372
|
+
continue;
|
|
1048
1373
|
}
|
|
1049
|
-
// 确保模块已加载
|
|
1050
1374
|
if (!mod.isLoaded()) {
|
|
1051
1375
|
try {
|
|
1052
1376
|
await mod.call('loadScript');
|
|
1053
1377
|
} catch (error) {
|
|
1054
|
-
this.log('error',
|
|
1055
|
-
continue;
|
|
1378
|
+
this.log('error', '模块' + info.name + '加载失败', error);
|
|
1379
|
+
continue;
|
|
1056
1380
|
}
|
|
1057
1381
|
}
|
|
1058
|
-
|
|
1059
|
-
// 执行方法
|
|
1060
1382
|
try {
|
|
1061
|
-
|
|
1383
|
+
var ret = await mod.call.apply(mod, [method].concat(params));
|
|
1062
1384
|
if (ret !== null && ret !== undefined) {
|
|
1063
1385
|
result = ret;
|
|
1064
1386
|
}
|
|
1065
1387
|
} catch (error) {
|
|
1066
|
-
this.log('error',
|
|
1388
|
+
this.log('error', '模块' + info.name + '执行' + method + '方法失败', error);
|
|
1067
1389
|
}
|
|
1068
1390
|
}
|
|
1069
1391
|
|
|
@@ -1073,28 +1395,23 @@ Manager.prototype.runWait = async function (method, ...params) {
|
|
|
1073
1395
|
/**
|
|
1074
1396
|
* 异步触发事件,并发执行不等待
|
|
1075
1397
|
* @param {string} method 方法名称
|
|
1076
|
-
* @param
|
|
1398
|
+
* @param {...any} params 事件参数
|
|
1077
1399
|
* @returns {boolean} 是否成功触发事件
|
|
1078
1400
|
*/
|
|
1079
|
-
Manager.prototype.runAsync = async function (method
|
|
1080
|
-
// 参数校验
|
|
1401
|
+
Manager.prototype.runAsync = async function (method) {
|
|
1081
1402
|
if (typeof method !== 'string') {
|
|
1082
1403
|
throw new TypeError('方法名称必须是字符串');
|
|
1083
1404
|
}
|
|
1405
|
+
var params = Array.prototype.slice.call(arguments, 1);
|
|
1406
|
+
var infos = this.getInfos();
|
|
1084
1407
|
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
let infos = this.getInfos();
|
|
1089
|
-
|
|
1090
|
-
// 并发执行所有方法,不等待结果
|
|
1091
|
-
for (let i = 0; i < infos.length; i++) {
|
|
1092
|
-
let info = infos[i];
|
|
1093
|
-
let mod = this.getMod(info.name);
|
|
1408
|
+
for (var i = 0; i < infos.length; i++) {
|
|
1409
|
+
var info = infos[i];
|
|
1410
|
+
var mod = this.getMod(info.name);
|
|
1094
1411
|
try {
|
|
1095
|
-
mod.
|
|
1412
|
+
mod.call.apply(mod, [method].concat(params));
|
|
1096
1413
|
} catch (error) {
|
|
1097
|
-
this.log('error',
|
|
1414
|
+
this.log('error', '模块' + info.name + '执行' + method + '方法失败', error);
|
|
1098
1415
|
}
|
|
1099
1416
|
}
|
|
1100
1417
|
|
|
@@ -1104,28 +1421,23 @@ Manager.prototype.runAsync = async function (method, ...params) {
|
|
|
1104
1421
|
/**
|
|
1105
1422
|
* 异步触发事件,并发执行等待完成
|
|
1106
1423
|
* @param {string} method 方法名称
|
|
1107
|
-
* @param
|
|
1108
|
-
* @returns {Promise}
|
|
1424
|
+
* @param {...any} params 事件参数
|
|
1425
|
+
* @returns {Promise<Array>} 事件触发结果数组
|
|
1109
1426
|
*/
|
|
1110
|
-
Manager.prototype.runAll = async function (method
|
|
1111
|
-
// 参数校验
|
|
1427
|
+
Manager.prototype.runAll = async function (method) {
|
|
1112
1428
|
if (typeof method !== 'string') {
|
|
1113
1429
|
throw new TypeError('方法名称必须是字符串');
|
|
1114
1430
|
}
|
|
1431
|
+
var params = Array.prototype.slice.call(arguments, 1);
|
|
1432
|
+
var promises = [];
|
|
1433
|
+
var infos = this.getInfos();
|
|
1115
1434
|
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
const promises = [];
|
|
1120
|
-
let infos = this.getInfos();
|
|
1121
|
-
|
|
1122
|
-
// 并发执行所有方法
|
|
1123
|
-
for (let i = 0; i < infos.length; i++) {
|
|
1124
|
-
let info = infos[i];
|
|
1125
|
-
let mod = this.getMod(info.name);
|
|
1435
|
+
for (var i = 0; i < infos.length; i++) {
|
|
1436
|
+
var info = infos[i];
|
|
1437
|
+
var mod = this.getMod(info.name);
|
|
1126
1438
|
promises.push(
|
|
1127
|
-
mod.
|
|
1128
|
-
return null;
|
|
1439
|
+
mod.call.apply(mod, [method].concat(params)).catch(() => {
|
|
1440
|
+
return null;
|
|
1129
1441
|
})
|
|
1130
1442
|
);
|
|
1131
1443
|
}
|
|
@@ -1136,29 +1448,22 @@ Manager.prototype.runAll = async function (method, ...params) {
|
|
|
1136
1448
|
/**
|
|
1137
1449
|
* 异步触发事件,竞争执行
|
|
1138
1450
|
* @param {string} method 方法名称
|
|
1139
|
-
* @param
|
|
1140
|
-
* @returns {Promise} 第一个完成的结果
|
|
1451
|
+
* @param {...any} params 事件参数
|
|
1452
|
+
* @returns {Promise<*>} 第一个完成的结果
|
|
1141
1453
|
*/
|
|
1142
|
-
Manager.prototype.runRace = async function (method
|
|
1143
|
-
// 参数校验
|
|
1454
|
+
Manager.prototype.runRace = async function (method) {
|
|
1144
1455
|
if (typeof method !== 'string') {
|
|
1145
1456
|
throw new TypeError('方法名称必须是字符串');
|
|
1146
1457
|
}
|
|
1458
|
+
var params = Array.prototype.slice.call(arguments, 1);
|
|
1459
|
+
var promises = [];
|
|
1460
|
+
var infos = this.getInfos();
|
|
1147
1461
|
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
const promises = [];
|
|
1152
|
-
let infos = this.getInfos();
|
|
1153
|
-
|
|
1154
|
-
// 竞争执行所有方法
|
|
1155
|
-
for (let i = 0; i < infos.length; i++) {
|
|
1156
|
-
let info = infos[i];
|
|
1157
|
-
let mod = this.getMod(info.name);
|
|
1462
|
+
for (var i = 0; i < infos.length; i++) {
|
|
1463
|
+
var info = infos[i];
|
|
1464
|
+
var mod = this.getMod(info.name);
|
|
1158
1465
|
promises.push(
|
|
1159
|
-
mod.
|
|
1160
|
-
throw error; // 竞争执行中,失败应该抛出错误
|
|
1161
|
-
})
|
|
1466
|
+
mod.call.apply(mod, [method].concat(params))
|
|
1162
1467
|
);
|
|
1163
1468
|
}
|
|
1164
1469
|
|
|
@@ -1169,62 +1474,34 @@ Manager.prototype.runRace = async function (method, ...params) {
|
|
|
1169
1474
|
* 异步触发事件,瀑布流执行
|
|
1170
1475
|
* @param {string} method 方法名称
|
|
1171
1476
|
* @param {*} result 初始值
|
|
1172
|
-
* @param
|
|
1173
|
-
* @returns {Promise} 最后一个事件监听者的结果
|
|
1477
|
+
* @param {...any} params 事件参数
|
|
1478
|
+
* @returns {Promise<*>} 最后一个事件监听者的结果
|
|
1174
1479
|
*/
|
|
1175
|
-
Manager.prototype.runWaterfall = async function (method, result
|
|
1176
|
-
// 参数校验
|
|
1480
|
+
Manager.prototype.runWaterfall = async function (method, result) {
|
|
1177
1481
|
if (typeof method !== 'string') {
|
|
1178
1482
|
throw new TypeError('方法名称必须是字符串');
|
|
1179
1483
|
}
|
|
1484
|
+
var params = Array.prototype.slice.call(arguments, 2);
|
|
1485
|
+
var infos = this.getInfos();
|
|
1486
|
+
var final_result = result;
|
|
1180
1487
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
// 确保模块已加载
|
|
1188
|
-
if (!mod.isLoaded()) {
|
|
1189
|
-
try {
|
|
1190
|
-
await mod.call('loadScript');
|
|
1191
|
-
} catch (error) {
|
|
1192
|
-
this.log('error', `模块${info.name}加载失败`, error);
|
|
1193
|
-
continue; // 跳过加载失败的模块
|
|
1194
|
-
}
|
|
1488
|
+
for (var i = 0; i < infos.length; i++) {
|
|
1489
|
+
var info = infos[i];
|
|
1490
|
+
var mod = this.getMod(info.name);
|
|
1491
|
+
if (!mod) {
|
|
1492
|
+
continue;
|
|
1195
1493
|
}
|
|
1196
|
-
|
|
1197
|
-
// 执行方法,将前一个结果作为参数传递给下一个
|
|
1198
1494
|
try {
|
|
1199
|
-
|
|
1495
|
+
var ret = await mod.call.apply(mod, [method, final_result].concat(params));
|
|
1200
1496
|
if (ret !== null && ret !== undefined) {
|
|
1201
|
-
|
|
1497
|
+
final_result = ret;
|
|
1202
1498
|
}
|
|
1203
1499
|
} catch (error) {
|
|
1204
|
-
this.log('error',
|
|
1500
|
+
this.log('error', '模块' + info.name + '执行' + method + '方法失败', error);
|
|
1205
1501
|
}
|
|
1206
1502
|
}
|
|
1207
1503
|
|
|
1208
|
-
return
|
|
1209
|
-
};
|
|
1210
|
-
|
|
1211
|
-
/**
|
|
1212
|
-
* 异步触发事件,子模块执行
|
|
1213
|
-
* @param {string} name 子模块名称
|
|
1214
|
-
* @param {string} method 方法名称
|
|
1215
|
-
* @param {...any} params 事件参数
|
|
1216
|
-
* @returns {Promise} 事件触发结果
|
|
1217
|
-
*/
|
|
1218
|
-
Manager.prototype.runSubMod = async function (name, method, ...params) {
|
|
1219
|
-
// 参数校验
|
|
1220
|
-
if (typeof method !== 'string') {
|
|
1221
|
-
throw new TypeError('方法名称必须是字符串');
|
|
1222
|
-
}
|
|
1223
|
-
let mod = this.getMod(name);
|
|
1224
|
-
if (!mod) {
|
|
1225
|
-
throw new Error(`模块${name}不存在`);
|
|
1226
|
-
}
|
|
1227
|
-
return await mod.do(method, ...params);
|
|
1504
|
+
return final_result;
|
|
1228
1505
|
};
|
|
1229
1506
|
|
|
1230
1507
|
/**
|
|
@@ -1232,75 +1509,18 @@ Manager.prototype.runSubMod = async function (name, method, ...params) {
|
|
|
1232
1509
|
* @param {string} event 事件名
|
|
1233
1510
|
*/
|
|
1234
1511
|
Manager.prototype.offEvent = function (event) {
|
|
1235
|
-
this.getEventer()
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
/**
|
|
1239
|
-
* 创建模块
|
|
1240
|
-
* @param {object} config 模块配置
|
|
1241
|
-
* @param {string} file 模块文件路径
|
|
1242
|
-
* @returns {string} 创建的模块文件路径
|
|
1243
|
-
*/
|
|
1244
|
-
Manager.prototype.createFile = function (config, file) {
|
|
1245
|
-
if (!config.name) {
|
|
1246
|
-
throw new TypeError('模块名称不能为空');
|
|
1247
|
-
}
|
|
1248
|
-
if (!config.title) {
|
|
1249
|
-
throw new TypeError('模块标题不能为空');
|
|
1250
|
-
}
|
|
1251
|
-
if (!config.description) {
|
|
1252
|
-
throw new TypeError('模块描述不能为空');
|
|
1253
|
-
}
|
|
1254
|
-
if (this.getMod(config.name)) {
|
|
1255
|
-
throw new Error(`创建失败,原因:模块${config.name}已存在`);
|
|
1512
|
+
var eventer = this.getEventer();
|
|
1513
|
+
if (eventer && eventer.off) {
|
|
1514
|
+
eventer.off(event);
|
|
1256
1515
|
}
|
|
1257
|
-
let mod = new this.Drive(config, this);
|
|
1258
|
-
let f = file;
|
|
1259
|
-
if (!f) {
|
|
1260
|
-
f = `${config.name}/${this.config.filename}`;
|
|
1261
|
-
if (this.config.search_way === 'dir') {
|
|
1262
|
-
let dir_name = this.getDirName();
|
|
1263
|
-
f = `${dir_name}/${f}`;
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
f = f.fullname(this.config.dir);
|
|
1267
|
-
mod.createConfigFile(f, config);
|
|
1268
|
-
return f;
|
|
1269
|
-
};
|
|
1270
|
-
|
|
1271
|
-
/**
|
|
1272
|
-
* 创建模块
|
|
1273
|
-
* @param {object} config 模块配置
|
|
1274
|
-
* @param {string} file 模块文件路径
|
|
1275
|
-
* @returns {object} 创建的模块实例
|
|
1276
|
-
*/
|
|
1277
|
-
Manager.prototype.create = function (config, file = '') {
|
|
1278
|
-
let f = this.createFile(config, file);
|
|
1279
|
-
let mod = this.registerMod(f, config);
|
|
1280
|
-
this.refreshSnapshot();
|
|
1281
|
-
return mod;
|
|
1282
|
-
};
|
|
1283
|
-
|
|
1284
|
-
/**
|
|
1285
|
-
* 删除模块
|
|
1286
|
-
* @param {string} name 模块名称
|
|
1287
|
-
* @returns {object} 删除的模块实例
|
|
1288
|
-
*/
|
|
1289
|
-
Manager.prototype.remove = async function (name) {
|
|
1290
|
-
let mod = this.getMod(name);
|
|
1291
|
-
if (mod) {
|
|
1292
|
-
await mod.remove();
|
|
1293
|
-
}
|
|
1294
|
-
this.unregisterMod(name);
|
|
1295
|
-
this.refreshSnapshot();
|
|
1296
|
-
return mod;
|
|
1297
1516
|
};
|
|
1298
1517
|
|
|
1299
1518
|
module.exports = {
|
|
1300
1519
|
Manager,
|
|
1301
1520
|
Drive,
|
|
1302
|
-
|
|
1521
|
+
// eslint-disable-next-line mm_eslint/variable-name
|
|
1522
|
+
Mod: require('./mod').Mod,
|
|
1303
1523
|
// 向后兼容
|
|
1304
1524
|
Index: Manager,
|
|
1305
1525
|
Item: Drive
|
|
1306
|
-
};
|
|
1526
|
+
};
|