mm_machine 2.9.3 → 2.9.5
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/dir_scanner.js +262 -0
- package/drive.js +668 -490
- package/id_gen.js +138 -0
- package/index.js +1141 -921
- package/mod.js +742 -711
- package/package.json +6 -2
- package/registry.js +634 -0
- package/word.js +209 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mm_machine",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.5",
|
|
4
4
|
"description": "A flexible Node.js plugin mechanism system for dynamic loading, management and execution of modules. Supports hot reload, lifecycle management, and modern JavaScript features.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -30,13 +30,17 @@
|
|
|
30
30
|
"mod.js",
|
|
31
31
|
"index.js",
|
|
32
32
|
"drive.js",
|
|
33
|
+
"dir_scanner.js",
|
|
34
|
+
"id_gen.js",
|
|
35
|
+
"registry.js",
|
|
36
|
+
"word.js",
|
|
33
37
|
"README.md"
|
|
34
38
|
],
|
|
35
39
|
"dependencies": {
|
|
36
40
|
"@prettier/sync": "^0.6.1",
|
|
37
41
|
"mm_config": "^2.4.5",
|
|
38
42
|
"mm_hot_reload": "^1.4.2",
|
|
39
|
-
"mm_tpl": "^2.6.
|
|
43
|
+
"mm_tpl": "^2.6.4"
|
|
40
44
|
},
|
|
41
45
|
"devDependencies": {
|
|
42
46
|
"eslint": "^10.7.0",
|
package/registry.js
ADDED
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry 注册表类
|
|
3
|
+
* 持久化存储所有模块的元数据,启动时一次 IO 读取即可恢复全部模块信息
|
|
4
|
+
* @class Registry
|
|
5
|
+
* @augments Drive
|
|
6
|
+
*/
|
|
7
|
+
var fs = require('fs');
|
|
8
|
+
var path = require('path');
|
|
9
|
+
var { Drive } = require('./drive');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 新增条目自动补全的默认值
|
|
13
|
+
*/
|
|
14
|
+
var defaultEntry = {
|
|
15
|
+
title: '',
|
|
16
|
+
description: '',
|
|
17
|
+
type: 'plugin',
|
|
18
|
+
version: '1.0.0',
|
|
19
|
+
sort: 100,
|
|
20
|
+
main: '',
|
|
21
|
+
enabled: true,
|
|
22
|
+
auto_start: false,
|
|
23
|
+
lazy: 'auto',
|
|
24
|
+
state: 'stopped',
|
|
25
|
+
// eslint-disable-next-line mm_eslint/variable-name
|
|
26
|
+
registered_at: '',
|
|
27
|
+
updated_at: ''
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
class Registry extends Drive {
|
|
31
|
+
/**
|
|
32
|
+
* 构造函数
|
|
33
|
+
* @param {object} manager Manager 实例(必需),用于注册模块
|
|
34
|
+
* @param {object} [cfg] 配置参数
|
|
35
|
+
* @param {string} [cfg.file] 注册表文件绝对路径,默认从 Manager 配置拼接
|
|
36
|
+
*/
|
|
37
|
+
constructor(manager, cfg) {
|
|
38
|
+
var config = cfg || {};
|
|
39
|
+
super(config);
|
|
40
|
+
this._manager = manager;
|
|
41
|
+
this._data = { version: '1.0', updated_at: '', modules: [] };
|
|
42
|
+
this._file = config.file || Registry._resolveFile(manager);
|
|
43
|
+
this._save_timer = null;
|
|
44
|
+
this._dirty = false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 解析注册表文件路径
|
|
50
|
+
* @param {object} manager Manager 实例
|
|
51
|
+
* @returns {string} 注册表文件绝对路径
|
|
52
|
+
* @private
|
|
53
|
+
* @static
|
|
54
|
+
*/
|
|
55
|
+
Registry._resolveFile = function (manager) {
|
|
56
|
+
// 有显式配置时直接使用
|
|
57
|
+
if (manager.config.registry_file) {
|
|
58
|
+
return path.resolve(manager.dir || process.cwd(), manager.config.registry_file);
|
|
59
|
+
}
|
|
60
|
+
// 默认:data/register/{构造函数名}_{config.name}.json
|
|
61
|
+
var base_dir = path.resolve(manager.dir || process.cwd(), 'data/register');
|
|
62
|
+
var class_name = manager.constructor.name || 'Manager';
|
|
63
|
+
var mod_name = manager.config.name || 'default';
|
|
64
|
+
return path.join(base_dir, class_name + '_' + mod_name + '.json');
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// ==================== 数据加载/保存 ====================
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 从文件加载注册表数据
|
|
71
|
+
* 使用 require() 加载(支持缓存),文件不存在返回空数据结构
|
|
72
|
+
* @returns {object} 注册表数据 { version, modules[] }
|
|
73
|
+
*/
|
|
74
|
+
Registry.prototype.load = function () {
|
|
75
|
+
try {
|
|
76
|
+
if (fs.existsSync(this._file)) {
|
|
77
|
+
var data = require(this._file);
|
|
78
|
+
if (data && data.modules) {
|
|
79
|
+
this._data = data;
|
|
80
|
+
return this._data;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} catch {
|
|
84
|
+
// 文件不存在或解析失败,使用空数据
|
|
85
|
+
}
|
|
86
|
+
this._data = { version: '1.0', updated_at: '', modules: [] };
|
|
87
|
+
return this._data;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 保存注册表数据到文件
|
|
92
|
+
* 防抖保存(500ms 内多次调用只写一次),写完后清除 require.cache
|
|
93
|
+
*/
|
|
94
|
+
Registry.prototype.save = function () {
|
|
95
|
+
this._dirty = true;
|
|
96
|
+
|
|
97
|
+
if (this._save_timer) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this._save_timer = setTimeout(() => {
|
|
102
|
+
this._save_timer = null;
|
|
103
|
+
this._flushSave();
|
|
104
|
+
}, 500);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 立即写入文件
|
|
109
|
+
* @private
|
|
110
|
+
*/
|
|
111
|
+
Registry.prototype._flushSave = function () {
|
|
112
|
+
if (!this._dirty) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this._data.updated_at = new Date().toISOString();
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
var dir = path.dirname(this._file);
|
|
120
|
+
if (!fs.existsSync(dir)) {
|
|
121
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
122
|
+
}
|
|
123
|
+
fs.writeFileSync(this._file, JSON.stringify(this._data, null, 2), 'utf8');
|
|
124
|
+
|
|
125
|
+
// 清除 require.cache 确保下次 load 读取最新数据
|
|
126
|
+
try {
|
|
127
|
+
delete require.cache[require.resolve(this._file)];
|
|
128
|
+
} catch {
|
|
129
|
+
// 忽略
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
this._dirty = false;
|
|
133
|
+
} catch {
|
|
134
|
+
// 保存失败,记录错误
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// ==================== 查询方法 ====================
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 获取所有注册表项的浅拷贝数组
|
|
142
|
+
* @returns {Array<object>} 注册表项数组
|
|
143
|
+
*/
|
|
144
|
+
Registry.prototype.getAllEntries = function () {
|
|
145
|
+
return this._data.modules.slice();
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 按名称获取单个注册表项
|
|
150
|
+
* @param {string} name 模块名称
|
|
151
|
+
* @returns {object|null} 注册表项,不存在返回 null
|
|
152
|
+
*/
|
|
153
|
+
Registry.prototype.getEntry = function (name) {
|
|
154
|
+
var modules = this._data.modules;
|
|
155
|
+
for (var i = 0; i < modules.length; i++) {
|
|
156
|
+
if (modules[i].name === name) {
|
|
157
|
+
return modules[i];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 获取注册表项数量
|
|
165
|
+
* @returns {number} 数量
|
|
166
|
+
*/
|
|
167
|
+
Registry.prototype.getEntryCount = function () {
|
|
168
|
+
return this._data.modules.length;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* 检查注册表项是否存在
|
|
173
|
+
* @param {string} name 模块名称
|
|
174
|
+
* @returns {boolean} 是否存在
|
|
175
|
+
*/
|
|
176
|
+
Registry.prototype.hasEntry = function (name) {
|
|
177
|
+
return this.getEntry(name) !== null;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// ==================== 增删改方法 ====================
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* 添加注册表项
|
|
184
|
+
* 自动补全缺失字段,然后保存
|
|
185
|
+
* @param {object} data 注册表项数据
|
|
186
|
+
*/
|
|
187
|
+
Registry.prototype.addEntry = function (data) {
|
|
188
|
+
if (!data || !data.name) {
|
|
189
|
+
throw new TypeError('注册表项必须包含 name 字段');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (this.hasEntry(data.name)) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
var now = new Date().toISOString();
|
|
197
|
+
var entry = {
|
|
198
|
+
...defaultEntry,
|
|
199
|
+
...data,
|
|
200
|
+
// eslint-disable-next-line mm_eslint/variable-name
|
|
201
|
+
registered_at: data.registered_at || now,
|
|
202
|
+
updated_at: now
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
this._data.modules.push(entry);
|
|
206
|
+
this.save();
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* 更新注册表项
|
|
211
|
+
* 部分合并,只覆盖 data 中存在的字段
|
|
212
|
+
* @param {string} name 模块名称
|
|
213
|
+
* @param {object} data 要更新的字段
|
|
214
|
+
*/
|
|
215
|
+
Registry.prototype.updateEntry = function (name, data) {
|
|
216
|
+
if (!data) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
var modules = this._data.modules;
|
|
221
|
+
for (var i = 0; i < modules.length; i++) {
|
|
222
|
+
if (modules[i].name === name) {
|
|
223
|
+
for (var k in data) {
|
|
224
|
+
modules[i][k] = data[k];
|
|
225
|
+
}
|
|
226
|
+
modules[i].updated_at = new Date().toISOString();
|
|
227
|
+
this.save();
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* 移除注册表项并保存
|
|
235
|
+
* @param {string} name 模块名称
|
|
236
|
+
*/
|
|
237
|
+
Registry.prototype.removeEntry = function (name) {
|
|
238
|
+
var modules = this._data.modules;
|
|
239
|
+
for (var i = 0; i < modules.length; i++) {
|
|
240
|
+
if (modules[i].name === name) {
|
|
241
|
+
modules.splice(i, 1);
|
|
242
|
+
this.save();
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// ==================== 自启动管理 ====================
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* 设置模块是否自启动
|
|
252
|
+
* @param {string} name 模块名称
|
|
253
|
+
* @param {boolean} flag 是否自启动
|
|
254
|
+
*/
|
|
255
|
+
Registry.prototype.setAutoStart = function (name, flag) {
|
|
256
|
+
this.updateEntry(name, { auto_start: !!flag });
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* 获取所有自启动模块列表
|
|
261
|
+
* @returns {Array<object>} 自启动模块列表
|
|
262
|
+
*/
|
|
263
|
+
Registry.prototype.getAutoStartList = function () {
|
|
264
|
+
var modules = this._data.modules;
|
|
265
|
+
var list = [];
|
|
266
|
+
for (var i = 0; i < modules.length; i++) {
|
|
267
|
+
if (modules[i].auto_start) {
|
|
268
|
+
list.push(modules[i]);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return list;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// ==================== 核心操作 ====================
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* 同步注册表数据到 Manager
|
|
278
|
+
* 遍历 _data.modules,从注册表数据直接创建模块实例(0 IO)
|
|
279
|
+
* 已存在的模块跳过(不重复创建)
|
|
280
|
+
*/
|
|
281
|
+
Registry.prototype.syncToManager = function () {
|
|
282
|
+
var modules = this._data.modules;
|
|
283
|
+
for (var i = 0; i < modules.length; i++) {
|
|
284
|
+
var entry = modules[i];
|
|
285
|
+
|
|
286
|
+
// 跳过 config_file 不存在的模块
|
|
287
|
+
if (entry.config_file && !fs.existsSync(entry.config_file)) {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (this._manager && typeof this._manager.registerModEx === 'function') {
|
|
292
|
+
this._manager.registerModEx(entry);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* 启动自启动模块
|
|
299
|
+
* 遍历 getAutoStartList(),调用 mod.load() 加载,
|
|
300
|
+
* 根据 lazy 策略决定是否继续 init/start
|
|
301
|
+
* @returns {Promise<void>}
|
|
302
|
+
*/
|
|
303
|
+
Registry.prototype.startAutoStart = async function () {
|
|
304
|
+
var list = this.getAutoStartList();
|
|
305
|
+
|
|
306
|
+
for (var i = 0; i < list.length; i++) {
|
|
307
|
+
var entry = list[i];
|
|
308
|
+
var mod = this._manager ? this._manager.getMod(entry.name) : null;
|
|
309
|
+
if (!mod) {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
// 加载模块
|
|
315
|
+
await mod.load();
|
|
316
|
+
|
|
317
|
+
// 根据 lazy 策略决定是否继续
|
|
318
|
+
if (this._shouldContinue(entry)) {
|
|
319
|
+
await mod.init();
|
|
320
|
+
await mod.start();
|
|
321
|
+
}
|
|
322
|
+
} catch {
|
|
323
|
+
// 单个模块启动失败不影响其他模块
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* 判断是否继续推进生命周期(init + start)
|
|
330
|
+
* @param {object} entry 注册表项
|
|
331
|
+
* @returns {boolean} 是否继续
|
|
332
|
+
* @private
|
|
333
|
+
*/
|
|
334
|
+
Registry.prototype._shouldContinue = function (entry) {
|
|
335
|
+
var lazy = entry.lazy !== undefined ? entry.lazy : 'auto';
|
|
336
|
+
|
|
337
|
+
if (lazy === false) {
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
if (lazy === 'auto') {
|
|
341
|
+
var mgr_lazy = this._manager ? this._manager.config.lazy_load : false;
|
|
342
|
+
return mgr_lazy === false || mgr_lazy === 'auto';
|
|
343
|
+
}
|
|
344
|
+
return false;
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* 启动引导
|
|
349
|
+
* load() → syncToManager() → 注册表为空时扫描 → startAutoStart() → save()
|
|
350
|
+
* @returns {Promise<void>}
|
|
351
|
+
*/
|
|
352
|
+
Registry.prototype.boot = async function () {
|
|
353
|
+
// 1. 加载注册表文件
|
|
354
|
+
this.load();
|
|
355
|
+
|
|
356
|
+
// 2. 注册模块到 Manager
|
|
357
|
+
this.syncToManager();
|
|
358
|
+
|
|
359
|
+
// 3. 注册表为空时扫描文件系统(首次启动)
|
|
360
|
+
// 实际扫描由 Manager.boot() 在调用 registry.boot() 后处理
|
|
361
|
+
|
|
362
|
+
// 4. 启动自启动模块
|
|
363
|
+
await this.startAutoStart();
|
|
364
|
+
|
|
365
|
+
// 5. 保存注册表
|
|
366
|
+
this.save();
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* 一键注册多个模块
|
|
371
|
+
* @param {Array<string|object>} files 文件路径数组或 {file, config} 对象数组
|
|
372
|
+
* @returns {number} 成功注册的模块数量
|
|
373
|
+
*/
|
|
374
|
+
Registry.prototype.registerAll = function (files) {
|
|
375
|
+
if (!files || !Array.isArray(files)) {
|
|
376
|
+
return 0;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
var count = 0;
|
|
380
|
+
|
|
381
|
+
for (var i = 0; i < files.length; i++) {
|
|
382
|
+
if (this._registerOne(files[i])) {
|
|
383
|
+
count++;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return count;
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* 注册单个文件条目
|
|
392
|
+
* @param {string|object} item 文件路径或 {file, config} 对象
|
|
393
|
+
* @returns {boolean} 是否注册成功
|
|
394
|
+
* @private
|
|
395
|
+
*/
|
|
396
|
+
// eslint-disable-next-line complexity, max-lines-per-function
|
|
397
|
+
Registry.prototype._registerOne = function (item) {
|
|
398
|
+
var file_path = '';
|
|
399
|
+
var cfg = null;
|
|
400
|
+
|
|
401
|
+
if (typeof item === 'string') {
|
|
402
|
+
file_path = item;
|
|
403
|
+
} else if (item && item.file) {
|
|
404
|
+
file_path = item.file;
|
|
405
|
+
cfg = item.config || null;
|
|
406
|
+
} else {
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
// 未提供配置时从文件读取
|
|
412
|
+
if (!cfg) {
|
|
413
|
+
var content = fs.readFileSync(file_path, 'utf8');
|
|
414
|
+
cfg = JSON.parse(content);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (!cfg || !cfg.name) {
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
var entry = {
|
|
422
|
+
name: cfg.name,
|
|
423
|
+
title: cfg.title || '',
|
|
424
|
+
description: cfg.description || '',
|
|
425
|
+
type: cfg.type || 'plugin',
|
|
426
|
+
version: cfg.version || '1.0.0',
|
|
427
|
+
sort: cfg.sort || 100,
|
|
428
|
+
config_file: file_path,
|
|
429
|
+
main: cfg.main || '',
|
|
430
|
+
enabled: cfg.enabled !== false,
|
|
431
|
+
auto_start: !!cfg.auto_start,
|
|
432
|
+
lazy: cfg.lazy !== undefined ? cfg.lazy : 'auto'
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
this.addEntry(entry);
|
|
436
|
+
|
|
437
|
+
if (this._manager && typeof this._manager.registerModEx === 'function') {
|
|
438
|
+
this._manager.registerModEx(entry);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return true;
|
|
442
|
+
} catch {
|
|
443
|
+
// 单个模块注册失败不影响其他模块
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* 获取全量模块目录
|
|
450
|
+
* 三步交叉比对:扫描文件系统 → 交叉比对注册表 → 补充运行时状态
|
|
451
|
+
* @param {object} scanner DirScanner 实例
|
|
452
|
+
* @param {string[]} dirs 要扫描的目录列表
|
|
453
|
+
* @param {boolean} [force] 是否强制重新扫描文件系统
|
|
454
|
+
* @returns {Array<object>} 全量目录列表
|
|
455
|
+
*/
|
|
456
|
+
Registry.prototype.getCatalog = function (scanner, dirs, force) {
|
|
457
|
+
if (!scanner || typeof scanner.scanAndParse !== 'function') {
|
|
458
|
+
throw new TypeError('scanner 必须包含 scanAndParse 方法');
|
|
459
|
+
}
|
|
460
|
+
if (!dirs || !Array.isArray(dirs)) {
|
|
461
|
+
return [];
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// 1. 扫描文件系统
|
|
465
|
+
var file_dict = this._scanFileSystem(scanner, dirs, force);
|
|
466
|
+
|
|
467
|
+
// 2. 与注册表交叉比对
|
|
468
|
+
var registry_dict = this._buildRegistryMap();
|
|
469
|
+
|
|
470
|
+
// 3. 构建全量目录
|
|
471
|
+
var result = this._buildCatalog(file_dict, registry_dict);
|
|
472
|
+
|
|
473
|
+
// 4. 补充注册表中有但文件系统中不存在的模块
|
|
474
|
+
this._appendMissingModules(result.catalog, result.processed, registry_dict);
|
|
475
|
+
|
|
476
|
+
return result.catalog;
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* 扫描文件系统,构建文件映射
|
|
481
|
+
* @param {object} scanner DirScanner 实例
|
|
482
|
+
* @param {string[]} dirs 目录列表
|
|
483
|
+
* @param {boolean} force 是否强制扫描
|
|
484
|
+
* @returns {object} 文件路径到配置的映射
|
|
485
|
+
* @private
|
|
486
|
+
*/
|
|
487
|
+
Registry.prototype._scanFileSystem = function (scanner, dirs, force) {
|
|
488
|
+
var file_dict = {};
|
|
489
|
+
for (var i = 0; i < dirs.length; i++) {
|
|
490
|
+
var dir = dirs[i];
|
|
491
|
+
var entries = scanner.scanAndParse(dir, '*.json', null, !!force);
|
|
492
|
+
for (var j = 0; j < entries.length; j++) {
|
|
493
|
+
var entry = entries[j];
|
|
494
|
+
file_dict[entry.file] = entry.config;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return file_dict;
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* 构建注册表名称映射
|
|
502
|
+
* @returns {object} 名称到注册表项的映射
|
|
503
|
+
* @private
|
|
504
|
+
*/
|
|
505
|
+
Registry.prototype._buildRegistryMap = function () {
|
|
506
|
+
var registry_dict = {};
|
|
507
|
+
var reg_modules = this._data.modules;
|
|
508
|
+
for (var k = 0; k < reg_modules.length; k++) {
|
|
509
|
+
var reg = reg_modules[k];
|
|
510
|
+
registry_dict[reg.name] = reg;
|
|
511
|
+
}
|
|
512
|
+
return registry_dict;
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* 从文件系统扫描结果构建目录条目
|
|
517
|
+
* @param {object} file_dict 文件映射
|
|
518
|
+
* @param {object} registry_dict 注册表映射
|
|
519
|
+
* @returns {Array} 目录条目列表
|
|
520
|
+
* @private
|
|
521
|
+
*/
|
|
522
|
+
Registry.prototype._buildCatalog = function (file_dict, registry_dict) {
|
|
523
|
+
var catalog = [];
|
|
524
|
+
var processed = {};
|
|
525
|
+
|
|
526
|
+
for (var file_path in file_dict) {
|
|
527
|
+
var cfg = file_dict[file_path];
|
|
528
|
+
var name = cfg.name || path.basename(file_path, path.extname(file_path));
|
|
529
|
+
var reg_ref = registry_dict[name] || null;
|
|
530
|
+
|
|
531
|
+
var item = {
|
|
532
|
+
name: name,
|
|
533
|
+
title: cfg.title || '',
|
|
534
|
+
type: cfg.type || 'plugin',
|
|
535
|
+
sort: cfg.sort || 100,
|
|
536
|
+
config_file: file_path,
|
|
537
|
+
main: cfg.main || '',
|
|
538
|
+
registered: !!reg_ref,
|
|
539
|
+
auto_start: reg_ref ? !!reg_ref.auto_start : false,
|
|
540
|
+
lazy: reg_ref ? reg_ref.lazy : 'auto',
|
|
541
|
+
// eslint-disable-next-line mm_eslint/variable-name
|
|
542
|
+
lifecycle_state: null,
|
|
543
|
+
file_exists: true,
|
|
544
|
+
status: 'unregistered'
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
this._addRuntimeState(item, reg_ref, name);
|
|
548
|
+
|
|
549
|
+
catalog.push(item);
|
|
550
|
+
processed[name] = true;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return { catalog: catalog, processed: processed };
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* 补充运行时状态
|
|
558
|
+
* @param {object} item 目录条目
|
|
559
|
+
* @param {object|null} reg_ref 注册表项
|
|
560
|
+
* @param {string} name 模块名称
|
|
561
|
+
* @private
|
|
562
|
+
*/
|
|
563
|
+
Registry.prototype._addRuntimeState = function (item, reg_ref, name) {
|
|
564
|
+
if (!reg_ref || !this._manager) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
var mod = this._manager.getMod(name);
|
|
569
|
+
if (mod) {
|
|
570
|
+
// eslint-disable-next-line no-param-reassign
|
|
571
|
+
item.lifecycle_state = mod.lifecycle_state;
|
|
572
|
+
// eslint-disable-next-line no-param-reassign
|
|
573
|
+
item.status = Registry._calcStatus(mod.lifecycle_state);
|
|
574
|
+
} else {
|
|
575
|
+
// eslint-disable-next-line no-param-reassign
|
|
576
|
+
item.status = 'stopped';
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* 追加注册表中有但文件系统中不存在的模块
|
|
582
|
+
* @param {Array} catalog 目录列表
|
|
583
|
+
* @param {object} processed 已处理模块名称集合
|
|
584
|
+
* @param {object} registry_dict 注册表映射
|
|
585
|
+
* @private
|
|
586
|
+
*/
|
|
587
|
+
// eslint-disable-next-line complexity
|
|
588
|
+
Registry.prototype._appendMissingModules = function (catalog, processed, registry_dict) {
|
|
589
|
+
var reg_modules = this._data.modules;
|
|
590
|
+
|
|
591
|
+
for (var n = 0; n < reg_modules.length; n++) {
|
|
592
|
+
var reg = reg_modules[n];
|
|
593
|
+
if (processed[reg.name]) {
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
var mod = this._manager ? this._manager.getMod(reg.name) : null;
|
|
598
|
+
catalog.push({
|
|
599
|
+
name: reg.name,
|
|
600
|
+
title: reg.title || '',
|
|
601
|
+
type: reg.type || 'plugin',
|
|
602
|
+
sort: reg.sort || 100,
|
|
603
|
+
config_file: reg.config_file || '',
|
|
604
|
+
main: reg.main || '',
|
|
605
|
+
registered: true,
|
|
606
|
+
auto_start: !!reg.auto_start,
|
|
607
|
+
lazy: reg.lazy || 'auto',
|
|
608
|
+
// eslint-disable-next-line mm_eslint/variable-name
|
|
609
|
+
lifecycle_state: mod ? mod.lifecycle_state : null,
|
|
610
|
+
file_exists: false,
|
|
611
|
+
status: 'file_missing'
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* 根据 lifecycle_state 计算状态
|
|
618
|
+
* @param {string} state 生命周期状态
|
|
619
|
+
* @returns {string} 状态值
|
|
620
|
+
* @private
|
|
621
|
+
* @static
|
|
622
|
+
*/
|
|
623
|
+
Registry._calcStatus = function (state) {
|
|
624
|
+
switch (state) {
|
|
625
|
+
case 'started':
|
|
626
|
+
return 'running';
|
|
627
|
+
case 'paused':
|
|
628
|
+
return 'paused';
|
|
629
|
+
default:
|
|
630
|
+
return 'stopped';
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
module.exports = { Registry };
|