mm_machine 2.2.6 → 2.2.8
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 +598 -0
- package/index.js +10 -10
- package/mod.js +480 -0
- package/package.json +3 -3
package/drive.js
ADDED
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
const {
|
|
2
|
+
Mod
|
|
3
|
+
} = require('./mod');
|
|
4
|
+
require('mm_tpl');
|
|
5
|
+
let {
|
|
6
|
+
conf
|
|
7
|
+
} = require('mm_config');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 驱动基础类
|
|
11
|
+
* @class
|
|
12
|
+
*/
|
|
13
|
+
class Drive extends Mod {
|
|
14
|
+
// 模块基础目录,用于放置脚本模板文件和配置模板文件
|
|
15
|
+
static base_dir = '';
|
|
16
|
+
|
|
17
|
+
// 配置项
|
|
18
|
+
static config = {
|
|
19
|
+
// 名称, 由中英文和下"_"组成, 用于卸载接口 例如: demo
|
|
20
|
+
name: '',
|
|
21
|
+
// 标题, 介绍作用
|
|
22
|
+
title: '',
|
|
23
|
+
// 描述, 用于描述该有什么用的
|
|
24
|
+
description: '',
|
|
25
|
+
// 文件路径, 当调用函数不存在时,会先从文件中加载
|
|
26
|
+
main: './index.js',
|
|
27
|
+
// 回调函数名 用于决定调用脚本的哪个函数
|
|
28
|
+
func_name: 'main',
|
|
29
|
+
// 作用域(同时作为检索的后缀名)
|
|
30
|
+
scope: ($.val && $.val.scope) ? $.val.scope : 'server',
|
|
31
|
+
// 排序
|
|
32
|
+
sort: 10,
|
|
33
|
+
// 状态, 0表示未启用, 1表示启用
|
|
34
|
+
state: 1,
|
|
35
|
+
// 显示, 0表示不显示, 1表示显示
|
|
36
|
+
show: 0,
|
|
37
|
+
// 是否结束,可选,默认false
|
|
38
|
+
end: false
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 构造函数
|
|
43
|
+
* @param {object} config 配置项
|
|
44
|
+
* @param {object} parent 父项
|
|
45
|
+
* @class
|
|
46
|
+
*/
|
|
47
|
+
constructor(config, parent) {
|
|
48
|
+
super(config, parent);
|
|
49
|
+
// 当前配置文件路径
|
|
50
|
+
this.config_file = '';
|
|
51
|
+
|
|
52
|
+
// 当前脚本文件路径
|
|
53
|
+
this.script_file = '';
|
|
54
|
+
|
|
55
|
+
// 模式: 1.生产模式 2.热更新模式 3.热重载模式 4.热更新+重载模式 5.重载模式
|
|
56
|
+
this.mode = 1;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 配置驱动
|
|
62
|
+
*/
|
|
63
|
+
Drive.prototype._preset = function () {
|
|
64
|
+
let file = this.config.main;
|
|
65
|
+
if (file) {
|
|
66
|
+
this.script_file = file.fullname(this._getDir());
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 获取配置
|
|
72
|
+
* @param {string} file 配置文件路径
|
|
73
|
+
* @returns {object} 配置项
|
|
74
|
+
*/
|
|
75
|
+
Drive.prototype.loadConfig = function (file) {
|
|
76
|
+
if (file) {
|
|
77
|
+
this.config_file = file;
|
|
78
|
+
};
|
|
79
|
+
// 加载配置
|
|
80
|
+
let config = $.loadJson(this.config_file, (cg) => {
|
|
81
|
+
this.setConfig(cg);
|
|
82
|
+
return cg;
|
|
83
|
+
});
|
|
84
|
+
if (!config) {
|
|
85
|
+
this.newConfig(this.config_file);
|
|
86
|
+
config = $.loadJson(this.config_file, (cg) => {
|
|
87
|
+
this.setConfig(cg);
|
|
88
|
+
return cg;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
this.setConfig(config);
|
|
92
|
+
return config;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 设置配置
|
|
97
|
+
* @param {object} config 配置对象
|
|
98
|
+
*/
|
|
99
|
+
Drive.prototype.setConfig = function (config) {
|
|
100
|
+
if (config) {
|
|
101
|
+
// 如果main改变,重置加载状态
|
|
102
|
+
if (config.main !== this.config.main) {
|
|
103
|
+
this.is_loaded = false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (this.config_file) {
|
|
107
|
+
// 合并配置并应用配置处理
|
|
108
|
+
this.config = conf({
|
|
109
|
+
...this.config,
|
|
110
|
+
...config
|
|
111
|
+
}, this.config_file);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
Object.assign(this.config, config);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
this._preset();
|
|
118
|
+
return this.config;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 获取配置
|
|
123
|
+
* @returns {object} 配置项
|
|
124
|
+
*/
|
|
125
|
+
Drive.prototype.getConfig = function () {
|
|
126
|
+
return this.config;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 获取当前文件
|
|
131
|
+
* @returns {object} 当前文件对象
|
|
132
|
+
*/
|
|
133
|
+
Drive.prototype._getDir = function () {
|
|
134
|
+
let dir = this._dir;
|
|
135
|
+
if (!dir) {
|
|
136
|
+
if (this.script_file) {
|
|
137
|
+
dir = this.script_file.dirname();
|
|
138
|
+
this._dir = dir;
|
|
139
|
+
}
|
|
140
|
+
else if (this.config_file) {
|
|
141
|
+
dir = this.config_file.dirname();
|
|
142
|
+
this._dir = dir;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return dir;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 获取模块目录
|
|
150
|
+
* @returns {string} 模块目录
|
|
151
|
+
*/
|
|
152
|
+
Drive.prototype._getBaseDir = function () {
|
|
153
|
+
return this.getParent().getBaseDir();
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 创建脚本
|
|
158
|
+
* @param {string} file 文件路径
|
|
159
|
+
* @param {object} model 模板数据
|
|
160
|
+
* @param {string} tpl_dir 模板文件路径
|
|
161
|
+
* @returns {boolean} 创建是否成功
|
|
162
|
+
*/
|
|
163
|
+
Drive.prototype._createScriptFile = function (file, model = {}, tpl_dir = '') {
|
|
164
|
+
var tpl = this.getScriptTpl(tpl_dir);
|
|
165
|
+
var content = $.tpl.render(tpl, model);
|
|
166
|
+
file.saveText(content);
|
|
167
|
+
return file;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 获取脚本模板
|
|
172
|
+
* @param {string} tpl_dir 模板文件路径
|
|
173
|
+
* @returns {string} 脚本模板内容
|
|
174
|
+
*/
|
|
175
|
+
Drive.prototype.getScriptTpl = function (tpl_dir = '') {
|
|
176
|
+
var f = './script.tpl.js'.fullname(tpl_dir || this._getBaseDir());
|
|
177
|
+
if (f.hasFile()) {
|
|
178
|
+
return f.loadText();
|
|
179
|
+
} else {
|
|
180
|
+
return `module.exports = {
|
|
181
|
+
/**
|
|
182
|
+
* 初始化
|
|
183
|
+
*/
|
|
184
|
+
async _init() {
|
|
185
|
+
this.log('debug', \`初始化!\`);
|
|
186
|
+
},
|
|
187
|
+
/**
|
|
188
|
+
* 销毁
|
|
189
|
+
*/
|
|
190
|
+
async _destroy(...args) {
|
|
191
|
+
this.log('debug', \`销毁!\`);
|
|
192
|
+
},
|
|
193
|
+
/**
|
|
194
|
+
* 主要逻辑
|
|
195
|
+
*/
|
|
196
|
+
async main(...args) {
|
|
197
|
+
// 主要代码写在这
|
|
198
|
+
}
|
|
199
|
+
}`;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 移除脚本
|
|
205
|
+
*/
|
|
206
|
+
Drive.prototype._remove = function () {
|
|
207
|
+
let file = this._getScriptFile();
|
|
208
|
+
if (!file) return;
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
if (this.mode === 3 || this.mode === 4) {
|
|
212
|
+
// 移除模块和监听
|
|
213
|
+
$.mod.unload(file);
|
|
214
|
+
} else {
|
|
215
|
+
// 移除模块缓存
|
|
216
|
+
let filename = require.resolve(file);
|
|
217
|
+
if (require.cache[filename]) {
|
|
218
|
+
require.cache[filename] = null;
|
|
219
|
+
delete require.cache[filename];
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
} catch (err) {
|
|
223
|
+
this.log('error', `移除脚本失败: `, err);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* 卸载脚本
|
|
229
|
+
*/
|
|
230
|
+
Drive.prototype.unloadScript = function () {
|
|
231
|
+
this._remove();
|
|
232
|
+
this.is_loaded = false;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* 重新加载脚本
|
|
237
|
+
* @returns {object | null} 返回加载的模块对象
|
|
238
|
+
*/
|
|
239
|
+
Drive.prototype.reloadScript = function () {
|
|
240
|
+
this.unloadScript();
|
|
241
|
+
return this.loadScript();
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* 卸载之后处理
|
|
246
|
+
* @param {boolean} remove 是否删除文件
|
|
247
|
+
*/
|
|
248
|
+
Drive.prototype.unloadCore = async function (remove) {
|
|
249
|
+
// 删除脚本
|
|
250
|
+
this.unloadScript();
|
|
251
|
+
if (remove) {
|
|
252
|
+
this.delFile();
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* 获取脚本文件路径
|
|
258
|
+
* @param {string} file 文件路径
|
|
259
|
+
* @returns {string | null} 完整文件路径
|
|
260
|
+
*/
|
|
261
|
+
Drive.prototype._getScriptFile = function () {
|
|
262
|
+
let main = this.config.main || '';
|
|
263
|
+
if (!main) return null;
|
|
264
|
+
|
|
265
|
+
let filename = main.fullname(this._getDir());
|
|
266
|
+
if (!filename.hasFile()) {
|
|
267
|
+
this.newScript(filename);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return filename;
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* 加载脚本文件,支持热重载模式
|
|
275
|
+
* 在开发模式(mode=3或4)下使用 $.require 进行热重载
|
|
276
|
+
* 在生产模式下使用标准的 require 加载
|
|
277
|
+
* @returns {object | null} 加载的模块对象
|
|
278
|
+
*/
|
|
279
|
+
Drive.prototype.loadScript = function () {
|
|
280
|
+
let file = this._getScriptFile();
|
|
281
|
+
if (!file) return null;
|
|
282
|
+
if (this.mode === 3 || this.mode === 4) {
|
|
283
|
+
// 开发模式:使用 $.require 进行热重载,绑定热更新回调
|
|
284
|
+
var cs = $.require(file, (mod) => {
|
|
285
|
+
this._setMainMethod(mod);
|
|
286
|
+
});
|
|
287
|
+
this._setMainMethod(cs);
|
|
288
|
+
return cs;
|
|
289
|
+
} else {
|
|
290
|
+
var cs = require(file);
|
|
291
|
+
this._setMainMethod(cs);
|
|
292
|
+
return cs;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* 设置主方法
|
|
298
|
+
* @param {object} mod 模块对象
|
|
299
|
+
*/
|
|
300
|
+
Drive.prototype._setMainMethod = function (mod) {
|
|
301
|
+
if (!mod) return;
|
|
302
|
+
let name = this.config.func_name || 'main';
|
|
303
|
+
if (name && mod[name]) {
|
|
304
|
+
this.main = mod[name];
|
|
305
|
+
}
|
|
306
|
+
if ($.push) {
|
|
307
|
+
$.push(this._methods, mod, true);
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
Object.assign(this._methods, mod);
|
|
311
|
+
}
|
|
312
|
+
this.is_loaded = true;
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* 获取配置文件作用域
|
|
317
|
+
* @param {object} file 文件对象
|
|
318
|
+
* @returns {string} 配置文件作用域
|
|
319
|
+
*/
|
|
320
|
+
Drive.prototype._getScope = function (file) {
|
|
321
|
+
return file.replace(/\\/g, '/').between('app/', '/');
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* 新建配置文件
|
|
326
|
+
* @param {string} file 目标文件路径
|
|
327
|
+
*/
|
|
328
|
+
Drive.prototype.newConfig = function (file) {
|
|
329
|
+
if (!this.config.name) {
|
|
330
|
+
this.config.name = this._getName(file);
|
|
331
|
+
this.config.scope = this._getScope(file);
|
|
332
|
+
}
|
|
333
|
+
this._createConfigFile(file);
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* 创建配置文件
|
|
338
|
+
* @param {string} file 目标文件路径
|
|
339
|
+
* @returns {string} 配置文件内容
|
|
340
|
+
*/
|
|
341
|
+
Drive.prototype._createConfigFile = function (file) {
|
|
342
|
+
var tpl = this.getConfigTpl();
|
|
343
|
+
var content = $.tpl.render(tpl, this.config);
|
|
344
|
+
file.saveText(content);
|
|
345
|
+
return file;
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* 获取配置模板
|
|
350
|
+
* @param {string} tpl_dir 模板文件路径
|
|
351
|
+
* @returns {string} 配置模板内容
|
|
352
|
+
*/
|
|
353
|
+
Drive.prototype.getConfigTpl = function (tpl_dir = '') {
|
|
354
|
+
var f = './config.tpl.json'.fullname(tpl_dir || this._getBaseDir());
|
|
355
|
+
if (f.hasFile()) {
|
|
356
|
+
return f.loadText();
|
|
357
|
+
} else {
|
|
358
|
+
return `{
|
|
359
|
+
// 模块名称,必填
|
|
360
|
+
"name": "\${name}",
|
|
361
|
+
// 模块标题,可选,默认"示例标题"
|
|
362
|
+
"title": "\${title || "示例标题"}",
|
|
363
|
+
// 模块描述,可选,默认"示例描述"
|
|
364
|
+
"description": "\${description || "示例描述"}",
|
|
365
|
+
// 主脚本文件路径,可选,默认"./index.js"
|
|
366
|
+
"main": "\${main || "./index.js"}",
|
|
367
|
+
// 模块作用域,可选,默认"server"
|
|
368
|
+
"scope": "\${scope || "server"}",
|
|
369
|
+
// 模块状态,可选,默认1
|
|
370
|
+
"state": \${state || 1},
|
|
371
|
+
// 模块排序,可选,默认100
|
|
372
|
+
"sort": \${sort || 100},
|
|
373
|
+
// 是否结束,可选,默认false
|
|
374
|
+
"end": \${end || false}
|
|
375
|
+
}`;
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* 加载配置文件
|
|
381
|
+
* @param {string} file 文件路径
|
|
382
|
+
* @param {string} name 配置项名称
|
|
383
|
+
* @returns {object | null} 配置对象
|
|
384
|
+
*/
|
|
385
|
+
Drive.prototype.loadFile = function (file, name) {
|
|
386
|
+
try {
|
|
387
|
+
let filename = file.fullname(this._getDir());
|
|
388
|
+
let text = filename.loadText();
|
|
389
|
+
|
|
390
|
+
// 如果文件不存在,创建新的配置文件
|
|
391
|
+
if (!text) {
|
|
392
|
+
this.newConfig(filename);
|
|
393
|
+
text = filename.loadText();
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (!text) return null;
|
|
397
|
+
|
|
398
|
+
let config = this._loadHotReload(filename);
|
|
399
|
+
let final_config = this._findConfigByName(config, name);
|
|
400
|
+
return final_config;
|
|
401
|
+
} catch (err) {
|
|
402
|
+
this.log('error', `加载配置文件失败: `, err);
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* 根据模式加载配置(支持热更新)
|
|
409
|
+
* @param {string} file 完整文件路径
|
|
410
|
+
* @returns {object | Array | null} 配置对象
|
|
411
|
+
*/
|
|
412
|
+
Drive.prototype._loadHotReload = function (file) {
|
|
413
|
+
if (this.mode === 2 || this.mode === 3 || this.mode === 4) {
|
|
414
|
+
return $.loadJson(file, this._handleHotReload.bind(this));
|
|
415
|
+
}
|
|
416
|
+
return file.loadJson();
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* 根据名称在配置数组中查找配置项
|
|
421
|
+
* @param {object | Array} config 配置对象或数组
|
|
422
|
+
* @param {string} name 配置项名称
|
|
423
|
+
* @returns {object | null} 匹配的配置项
|
|
424
|
+
*/
|
|
425
|
+
Drive.prototype._findConfigByName = function (config, name) {
|
|
426
|
+
if (!name || !Array.isArray(config)) return config;
|
|
427
|
+
return config.find((Drive) => Drive.name === name) || null;
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* 根据模式决定是否重新加载脚本
|
|
432
|
+
*/
|
|
433
|
+
Drive.prototype._reloadIfNeeded = function () {
|
|
434
|
+
if (this.mode === 3 || this.mode === 4) {
|
|
435
|
+
this.reloadScript();
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* 删除目录
|
|
441
|
+
*/
|
|
442
|
+
Drive.prototype.delDir = function () {
|
|
443
|
+
let main = this.config.main;
|
|
444
|
+
if (main && $.dir && $.dir.del) {
|
|
445
|
+
$.dir.del(this.dir);
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* 删除配置和脚本文件
|
|
451
|
+
* @returns {string | null} 错误消息,如果没有错误则返回null
|
|
452
|
+
*/
|
|
453
|
+
Drive.prototype.delFile = function () {
|
|
454
|
+
let name = this.config.name;
|
|
455
|
+
let file = this.script_file;
|
|
456
|
+
|
|
457
|
+
let error_message = null;
|
|
458
|
+
try {
|
|
459
|
+
if (!file || !file.hasFile) return null;
|
|
460
|
+
|
|
461
|
+
if (!file.hasFile()) {
|
|
462
|
+
return '配置文件不存在';
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
error_message = this._delConfigFile(file, name);
|
|
466
|
+
} catch (err) {
|
|
467
|
+
this.log('error', `删除文件失败: `, err);
|
|
468
|
+
error_message = `删除失败: ${err.message}`;
|
|
469
|
+
}
|
|
470
|
+
return error_message;
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* 删除配置文件
|
|
475
|
+
* @param {object} file 文件对象
|
|
476
|
+
* @param {string} name 配置名称
|
|
477
|
+
* @returns {string | null} 错误消息
|
|
478
|
+
*/
|
|
479
|
+
Drive.prototype._delConfigFile = function (file, name) {
|
|
480
|
+
let text = file.loadText();
|
|
481
|
+
if (!text) {
|
|
482
|
+
this.delDir();
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
let config = text.toJson();
|
|
487
|
+
if (Array.isArray(config)) {
|
|
488
|
+
return this._delArray(file, config, name);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
this.delDir();
|
|
492
|
+
return null;
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* 从数组配置中删除指定项
|
|
497
|
+
* @param {object} file 文件对象
|
|
498
|
+
* @param {Array} config 配置数组
|
|
499
|
+
* @param {string} name 配置名称
|
|
500
|
+
* @returns {string | null} 错误消息
|
|
501
|
+
*/
|
|
502
|
+
Drive.prototype._delArray = function (file, config, name) {
|
|
503
|
+
let index = config.findIndex((Drive) => Drive.name === name);
|
|
504
|
+
if (index === -1) return null;
|
|
505
|
+
|
|
506
|
+
this.delDir();
|
|
507
|
+
config.splice(index, 1);
|
|
508
|
+
|
|
509
|
+
if (config.length > 0) {
|
|
510
|
+
file.saveText(JSON.stringify(config, null, 4));
|
|
511
|
+
} else if (file.delFile) {
|
|
512
|
+
file.delFile();
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return null;
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* 获取配置文件名称
|
|
520
|
+
* @param {object} file 文件对象
|
|
521
|
+
* @returns {string} 配置文件名称
|
|
522
|
+
*/
|
|
523
|
+
Drive.prototype._getName = function (file) {
|
|
524
|
+
return file.dirname().basename();
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* 加载前处理
|
|
529
|
+
*/
|
|
530
|
+
Drive.prototype.loadBefore = async function () {
|
|
531
|
+
try {
|
|
532
|
+
let module = this.loadScript();
|
|
533
|
+
if (module) {
|
|
534
|
+
this.is_loaded = true;
|
|
535
|
+
}
|
|
536
|
+
} catch (err) {
|
|
537
|
+
this.log('error', `加载前处理失败: `, err);
|
|
538
|
+
this.is_loaded = false;
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* 加载处理
|
|
544
|
+
*/
|
|
545
|
+
Drive.prototype._loadCore = async function () {
|
|
546
|
+
this.loadConfig();
|
|
547
|
+
this.loadScript();
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* 保存配置
|
|
552
|
+
*/
|
|
553
|
+
Drive.prototype.save = function () {
|
|
554
|
+
try {
|
|
555
|
+
if (!this.script_file) return;
|
|
556
|
+
|
|
557
|
+
let file = this.script_file.fullname(this._getDir());
|
|
558
|
+
let text = file.loadText();
|
|
559
|
+
|
|
560
|
+
if (text && text.trim().startsWith('[')) {
|
|
561
|
+
// 数组格式配置
|
|
562
|
+
let config = text.toJson();
|
|
563
|
+
if (Array.isArray(config)) {
|
|
564
|
+
// 查找并更新现有配置项
|
|
565
|
+
let index = config.findIndex((Drive) => Drive.name === this.config.name);
|
|
566
|
+
if (index !== -1) {
|
|
567
|
+
config[index] = this.config;
|
|
568
|
+
} else {
|
|
569
|
+
// 如果不存在,添加新配置项
|
|
570
|
+
config.push(this.config);
|
|
571
|
+
}
|
|
572
|
+
file.saveText(JSON.stringify(config, null, 4));
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// 单对象格式配置,直接保存
|
|
578
|
+
file.saveText(JSON.stringify(this.config, null, 4));
|
|
579
|
+
} catch (err) {
|
|
580
|
+
this.log('error', `保存配置失败: `, err);
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* 触发事件
|
|
586
|
+
* @param {string} event 事件名
|
|
587
|
+
* @param {...any} args 事件参数
|
|
588
|
+
*/
|
|
589
|
+
Drive.prototype.emitEvent = function (event, ...args) {
|
|
590
|
+
this._eventer?.emit(event, ...args);
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* @module 导出Drive类
|
|
595
|
+
*/
|
|
596
|
+
module.exports = {
|
|
597
|
+
Drive
|
|
598
|
+
};
|
package/index.js
CHANGED
|
@@ -67,7 +67,7 @@ Manager.prototype.newInfo = function (name, sort, state) {
|
|
|
67
67
|
* @returns {string} 基础目录
|
|
68
68
|
*/
|
|
69
69
|
Manager.prototype.getBaseDir = function () {
|
|
70
|
-
return this.config.base_dir;
|
|
70
|
+
return this.config.base_dir || __dirname;
|
|
71
71
|
};
|
|
72
72
|
|
|
73
73
|
/**
|
|
@@ -94,8 +94,8 @@ Manager.prototype.updateInfo = function () {
|
|
|
94
94
|
Manager.prototype.sort = function () {
|
|
95
95
|
var sort_key = this.config.sort_key || 'sort';
|
|
96
96
|
this.infos.sort((o1, o2) => {
|
|
97
|
-
let p1 = o1[sort_key] ||
|
|
98
|
-
let p2 = o2[sort_key] ||
|
|
97
|
+
let p1 = o1[sort_key] || 100;
|
|
98
|
+
let p2 = o2[sort_key] || 100;
|
|
99
99
|
return p1 - p2;
|
|
100
100
|
});
|
|
101
101
|
};
|
|
@@ -188,7 +188,7 @@ Manager.prototype.register = function (config_file, config = {}, script = {}) {
|
|
|
188
188
|
Manager.prototype.load = async function (name) {
|
|
189
189
|
let mod = this.getMod(name);
|
|
190
190
|
if (mod) {
|
|
191
|
-
return await mod.
|
|
191
|
+
return await mod.do('load');
|
|
192
192
|
}
|
|
193
193
|
return null;
|
|
194
194
|
};
|
|
@@ -201,7 +201,7 @@ Manager.prototype.load = async function (name) {
|
|
|
201
201
|
Manager.prototype.unload = async function (name) {
|
|
202
202
|
let mod = this.getMod(name);
|
|
203
203
|
if (mod) {
|
|
204
|
-
return await mod.
|
|
204
|
+
return await mod.do('unload');
|
|
205
205
|
}
|
|
206
206
|
return null;
|
|
207
207
|
};
|
|
@@ -214,7 +214,7 @@ Manager.prototype.unload = async function (name) {
|
|
|
214
214
|
Manager.prototype.reload = async function (name) {
|
|
215
215
|
let mod = this.getMod(name);
|
|
216
216
|
if (mod) {
|
|
217
|
-
return await mod.
|
|
217
|
+
return await mod.do('reload');
|
|
218
218
|
}
|
|
219
219
|
return null;
|
|
220
220
|
};
|
|
@@ -251,7 +251,7 @@ Manager.prototype.loads = async function () {
|
|
|
251
251
|
var promises = [];
|
|
252
252
|
for (let name in this.mods) {
|
|
253
253
|
let mod = this.mods[name];
|
|
254
|
-
promises.push(mod.
|
|
254
|
+
promises.push(mod.do('load'));
|
|
255
255
|
}
|
|
256
256
|
await Promise.all(promises);
|
|
257
257
|
};
|
|
@@ -284,7 +284,7 @@ Manager.prototype.loadScripts = async function () {
|
|
|
284
284
|
var promises = [];
|
|
285
285
|
for (let name in this.mods) {
|
|
286
286
|
let mod = this.mods[name];
|
|
287
|
-
promises.push(mod.
|
|
287
|
+
promises.push(mod.call('loadScript'));
|
|
288
288
|
}
|
|
289
289
|
await Promise.all(promises);
|
|
290
290
|
};
|
|
@@ -431,11 +431,11 @@ Manager.prototype._runSub = async function (mod, method, ...params) {
|
|
|
431
431
|
try {
|
|
432
432
|
// 确保模块已加载
|
|
433
433
|
if (!mod.isLoaded()) {
|
|
434
|
-
await mod.
|
|
434
|
+
await mod.call('loadScript');
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
// 执行方法
|
|
438
|
-
let ret = await mod.
|
|
438
|
+
let ret = await mod.call(method, ...params);
|
|
439
439
|
// 根据模式决定是否重载
|
|
440
440
|
if (this.mode >= 4) {
|
|
441
441
|
mod.do('reload');
|
package/mod.js
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
const { Base } = require('mm_expand');
|
|
2
|
+
require('mm_hot_reload');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 增强require函数,专门用于脚本文件(.js)热重载
|
|
6
|
+
* 当脚本文件内容发生变化时,自动重新加载并触发回调
|
|
7
|
+
* @param {string} file 脚本文件路径(.js)
|
|
8
|
+
* @param {Function} func 热更新回调函数,文件变化时触发
|
|
9
|
+
* @returns {object} 加载的模块对象
|
|
10
|
+
* @throws {TypeError} 文件路径不是字符串或回调不是函数时抛出
|
|
11
|
+
*/
|
|
12
|
+
$.require = function (file, func) {
|
|
13
|
+
// 参数校验
|
|
14
|
+
if (typeof file !== 'string') {
|
|
15
|
+
throw new TypeError('文件路径必须是字符串');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (func && typeof func !== 'function') {
|
|
19
|
+
throw new TypeError('回调函数必须是函数');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// 确保文件是.js文件
|
|
23
|
+
if (!file.endsWith('.js')) {
|
|
24
|
+
throw new TypeError('$.require 只能用于.js脚本文件');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return $.mod.load(file, func);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 增强JSON加载函数,专门用于配置文件(.json)热重载
|
|
32
|
+
* 当配置文件内容发生变化时,自动重新加载并触发回调
|
|
33
|
+
* @param {string} file 配置文件路径(.json)
|
|
34
|
+
* @param {Function} func 热更新回调函数,文件变化时触发
|
|
35
|
+
* @returns {object} 解析的JSON对象
|
|
36
|
+
* @throws {TypeError} 文件路径不是字符串或回调不是函数时抛出
|
|
37
|
+
*/
|
|
38
|
+
$.loadJson = function (file, func) {
|
|
39
|
+
// 参数校验
|
|
40
|
+
if (typeof file !== 'string') {
|
|
41
|
+
throw new TypeError('文件路径必须是字符串');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (func && typeof func !== 'function') {
|
|
45
|
+
throw new TypeError('回调函数必须是函数');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 确保文件是.json文件
|
|
49
|
+
if (!file.endsWith('.json')) {
|
|
50
|
+
throw new TypeError('$.loadJson 只能用于.json配置文件');
|
|
51
|
+
}
|
|
52
|
+
return $.mod.load(file, func);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 模块类
|
|
57
|
+
*/
|
|
58
|
+
class Mod extends Base {
|
|
59
|
+
/**
|
|
60
|
+
* 配置项
|
|
61
|
+
*/
|
|
62
|
+
static config = {
|
|
63
|
+
// 模块名称
|
|
64
|
+
name: '',
|
|
65
|
+
// 模块描述
|
|
66
|
+
description: '',
|
|
67
|
+
// 状态, 可选值: 1-启用, 0-禁用
|
|
68
|
+
state: 1
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 构造函数
|
|
73
|
+
* @param {object} config 配置项
|
|
74
|
+
* @param {object|null} parent 父对象
|
|
75
|
+
*/
|
|
76
|
+
constructor(config, parent = null) {
|
|
77
|
+
super(config);
|
|
78
|
+
// 是否已加载
|
|
79
|
+
this.is_loaded = false;
|
|
80
|
+
// 追加查询到上级的方法
|
|
81
|
+
if (parent) {
|
|
82
|
+
this.getParent = function () {
|
|
83
|
+
return parent;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 检查模块是否已加载
|
|
91
|
+
* @returns {boolean} 是否已加载
|
|
92
|
+
*/
|
|
93
|
+
Mod.prototype.isLoaded = function () {
|
|
94
|
+
return this.is_loaded;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 日志输出
|
|
99
|
+
* @param {string} level 日志级别
|
|
100
|
+
* @param {string} message 日志消息
|
|
101
|
+
* @param {...any} params 日志参数
|
|
102
|
+
*/
|
|
103
|
+
Mod.prototype.log = function (level, message, ...params) {
|
|
104
|
+
this.getLogger()[level](`[${this.constructor.name}] [${this.config.name}] ${message}`, ...params);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 监听事件
|
|
109
|
+
*/
|
|
110
|
+
Mod.prototype._listen = function () {
|
|
111
|
+
this._onLoad();
|
|
112
|
+
this._onInit();
|
|
113
|
+
this._onStart();
|
|
114
|
+
this._onStop();
|
|
115
|
+
this._onUnload();
|
|
116
|
+
this._onDestroy();
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 监听销毁事件
|
|
121
|
+
*/
|
|
122
|
+
Mod.prototype._onDestroy = function () {
|
|
123
|
+
let status_last = 'stopped';
|
|
124
|
+
// 销毁
|
|
125
|
+
this.on('destroy:before', async (ctx) => {
|
|
126
|
+
status_last = this.getState();
|
|
127
|
+
this.setState('destroying');
|
|
128
|
+
});
|
|
129
|
+
this.on('destroy:after', async (ctx) => {
|
|
130
|
+
await this._destroyCore(...ctx.params);
|
|
131
|
+
});
|
|
132
|
+
this.on('destroy:success', (ctx) => {
|
|
133
|
+
this.setState('destroyed');
|
|
134
|
+
});
|
|
135
|
+
this.on('destroy:error', (ctx) => {
|
|
136
|
+
this.setState(status_last);
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 监听加载事件
|
|
142
|
+
* @param {object} ctx 上下文对象
|
|
143
|
+
*/
|
|
144
|
+
Mod.prototype._onLoad = async function (ctx) {
|
|
145
|
+
let status_last = 'created';
|
|
146
|
+
// 1. 加载
|
|
147
|
+
this.on('load:before', async (ctx) => {
|
|
148
|
+
status_last = this.getState();
|
|
149
|
+
this.setState('loading');
|
|
150
|
+
await this._loadCore(...ctx.params);
|
|
151
|
+
});
|
|
152
|
+
this.on('load:after', (ctx) => {
|
|
153
|
+
this.setState('loaded');
|
|
154
|
+
this.is_loaded = true;
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 监听启动事件
|
|
160
|
+
* @param {object} ctx 上下文对象
|
|
161
|
+
*/
|
|
162
|
+
Mod.prototype._onStart = async function (ctx) {
|
|
163
|
+
let status_last = 'inited';
|
|
164
|
+
// 3. 启动
|
|
165
|
+
this.on('start:before', async (ctx) => {
|
|
166
|
+
status_last = this.getState();
|
|
167
|
+
this.setState('starting');
|
|
168
|
+
await this._startCore(...ctx.params);
|
|
169
|
+
});
|
|
170
|
+
this.on('start:success', (ctx) => {
|
|
171
|
+
this.setState('running');
|
|
172
|
+
});
|
|
173
|
+
this.on('start:error', (ctx) => {
|
|
174
|
+
this.setState(status_last);
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 监听停止事件
|
|
180
|
+
* @param {object} ctx 上下文对象
|
|
181
|
+
*/
|
|
182
|
+
Mod.prototype._onStop = async function (ctx) {
|
|
183
|
+
let status_last = 'running';
|
|
184
|
+
// 4. 停止
|
|
185
|
+
this.on('stop:before', async (ctx) => {
|
|
186
|
+
status_last = this.getState();
|
|
187
|
+
this.setState('stopping');
|
|
188
|
+
});
|
|
189
|
+
this.on('stop:after', async (ctx) => {
|
|
190
|
+
await this._stopCore(...ctx.params);
|
|
191
|
+
});
|
|
192
|
+
this.on('stop:success', async (ctx) => {
|
|
193
|
+
this.setState('stopped');
|
|
194
|
+
});
|
|
195
|
+
this.on('stop:error', (ctx) => {
|
|
196
|
+
this.setState(status_last);
|
|
197
|
+
});
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* 监听卸载事件
|
|
202
|
+
* @param {object} ctx 上下文对象
|
|
203
|
+
*/
|
|
204
|
+
Mod.prototype._onUnload = async function (ctx) {
|
|
205
|
+
let status_last = 'stopped';
|
|
206
|
+
// 5. 卸载
|
|
207
|
+
this.on('unload:before', (ctx) => {
|
|
208
|
+
status_last = this.getState();
|
|
209
|
+
this.setState('unloading');
|
|
210
|
+
});
|
|
211
|
+
this.on('unload:check', (ctx) => {
|
|
212
|
+
if (status_last !== 'stopped') {
|
|
213
|
+
status_last = this.getState();
|
|
214
|
+
ctx.error = new Error('卸载前必须先停止');
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
this.on('unload:after', async (ctx) => {
|
|
218
|
+
this.setState('unloaded');
|
|
219
|
+
this.is_loaded = false;
|
|
220
|
+
await this._unloadCore(...ctx.params);
|
|
221
|
+
});
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* 初始化监听
|
|
226
|
+
*/
|
|
227
|
+
Mod.prototype._onDestroy = async function (ctx) {
|
|
228
|
+
let status_last = 'unloaded';
|
|
229
|
+
this.on('destroy:check', (ctx) => {
|
|
230
|
+
if (status_last !== 'unloaded') {
|
|
231
|
+
status_last = this.getState();
|
|
232
|
+
ctx.error = new Error('销毁前必须先卸载');
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 加载
|
|
239
|
+
*/
|
|
240
|
+
Mod.prototype.load = async function () {
|
|
241
|
+
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* 加载核心
|
|
246
|
+
*/
|
|
247
|
+
Mod.prototype._loadCore = async function () {
|
|
248
|
+
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* 启动
|
|
253
|
+
*/
|
|
254
|
+
Mod.prototype.start = async function () {
|
|
255
|
+
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* 启动核心
|
|
260
|
+
*/
|
|
261
|
+
Mod.prototype._startCore = async function () {
|
|
262
|
+
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* 停止
|
|
267
|
+
*/
|
|
268
|
+
Mod.prototype.stop = async function () {
|
|
269
|
+
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* 停止核心
|
|
274
|
+
*/
|
|
275
|
+
Mod.prototype._stopCore = async function () {
|
|
276
|
+
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* 卸载
|
|
281
|
+
*/
|
|
282
|
+
Mod.prototype.unload = async function () {
|
|
283
|
+
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* 卸载核心
|
|
288
|
+
*/
|
|
289
|
+
Mod.prototype._unloadCore = async function () {
|
|
290
|
+
if (this.methods) {
|
|
291
|
+
this.methods = {};
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* 重载
|
|
297
|
+
*/
|
|
298
|
+
Mod.prototype.reload = async function () {
|
|
299
|
+
await this.do('unload');
|
|
300
|
+
await this.do('load');
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* 重启
|
|
305
|
+
*/
|
|
306
|
+
Mod.prototype.restart = async function () {
|
|
307
|
+
await this.do('stop');
|
|
308
|
+
await this.do('start');
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* 是否运行中
|
|
313
|
+
* @returns {boolean} 是否运行中
|
|
314
|
+
*/
|
|
315
|
+
Mod.prototype.isRunning = function () {
|
|
316
|
+
return this.getState() === 'running';
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* 结束
|
|
321
|
+
*/
|
|
322
|
+
Mod.prototype.end = async function () {
|
|
323
|
+
await this.do('stop');
|
|
324
|
+
await this.do('unload');
|
|
325
|
+
await this.do('destroy');
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* 触发事件
|
|
330
|
+
* @param {string} event 事件名
|
|
331
|
+
* @param {...any} args 事件参数
|
|
332
|
+
*/
|
|
333
|
+
Mod.prototype.emitEvent = function (event, ...args) {
|
|
334
|
+
this._eventer?.emit(event, ...args);
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* 运行方法
|
|
339
|
+
* @param {string} method 方法名
|
|
340
|
+
* @param {...any} args 方法参数
|
|
341
|
+
* @returns {Promise<any>} 方法执行结果
|
|
342
|
+
*/
|
|
343
|
+
Mod.prototype.run = async function (method, ...args) {
|
|
344
|
+
return this.main(method, ...args);
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* 主方法
|
|
349
|
+
* @param {string} method 方法名
|
|
350
|
+
* @param {...any} args 方法参数
|
|
351
|
+
* @returns {Promise<any>} 方法执行结果
|
|
352
|
+
*/
|
|
353
|
+
Mod.prototype.main = async function (method, ...args) {
|
|
354
|
+
if (!this.methods?.[method]) {
|
|
355
|
+
throw new Error(`方法${method}不存在`);
|
|
356
|
+
}
|
|
357
|
+
return await this.methods[method](...args);
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* 设置脚本方法
|
|
362
|
+
* @param {object} methods 脚本方法对象
|
|
363
|
+
*/
|
|
364
|
+
Mod.prototype.setMethods = function (methods) {
|
|
365
|
+
if (!methods) return;
|
|
366
|
+
if (!this._methods) {
|
|
367
|
+
this._methods = {};
|
|
368
|
+
}
|
|
369
|
+
Object.assign(this._methods, methods);
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* 设置脚本方法
|
|
374
|
+
* @param {object} methods 脚本方法对象
|
|
375
|
+
*/
|
|
376
|
+
Mod.prototype.setMethod = function (method, func) {
|
|
377
|
+
if (!func) throw new Error(`函数(func)不能为空`);
|
|
378
|
+
if (!this._methods) {
|
|
379
|
+
this._methods = {};
|
|
380
|
+
}
|
|
381
|
+
this._methods[method] = func;
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* 获取方法函数
|
|
386
|
+
* @param {string} method 方法名
|
|
387
|
+
* @returns {Function|null} 方法函数
|
|
388
|
+
*/
|
|
389
|
+
Mod.prototype.getMethod = function (method) {
|
|
390
|
+
let func = this._methods && this._methods[method];
|
|
391
|
+
if (!func) {
|
|
392
|
+
func = this[method];
|
|
393
|
+
}
|
|
394
|
+
return func;
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* 调用函数(核心执行方法)
|
|
399
|
+
* @param {string} method 函数名
|
|
400
|
+
* @param {*} params 参数集合
|
|
401
|
+
* @returns {Promise<any>} 执行结果
|
|
402
|
+
*/
|
|
403
|
+
Mod.prototype.call = async function (method, ...params) {
|
|
404
|
+
let method_func = this.getMethod(method);
|
|
405
|
+
if (!method_func) return null;
|
|
406
|
+
|
|
407
|
+
let result;
|
|
408
|
+
|
|
409
|
+
try {
|
|
410
|
+
result = await this._callBefore(method, params);
|
|
411
|
+
if (result) return result;
|
|
412
|
+
result = await this._callMain(method_func, params, result);
|
|
413
|
+
result = await this._callAfter(method, params, result);
|
|
414
|
+
} catch (err) {
|
|
415
|
+
this.log('error', `执行方法 ${method} 失败: `, err);
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return result;
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* 执行前置钩子
|
|
424
|
+
* @param {string} method 方法名
|
|
425
|
+
* @param {Array} params 参数
|
|
426
|
+
* @returns {Promise<any>} 前置钩子结果
|
|
427
|
+
*/
|
|
428
|
+
Mod.prototype._callBefore = async function (method, params) {
|
|
429
|
+
let before_method = `${method}Before`;
|
|
430
|
+
let before_func = this.getMethod(before_method);
|
|
431
|
+
|
|
432
|
+
if (!before_func) return undefined;
|
|
433
|
+
|
|
434
|
+
try {
|
|
435
|
+
let result = await before_func.call(this._mod || this, ...params);
|
|
436
|
+
return result;
|
|
437
|
+
} catch (err) {
|
|
438
|
+
this.log('error', `执行前置钩子 ${before_method} 失败: `, err);
|
|
439
|
+
return undefined;
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* 执行主方法
|
|
445
|
+
* @param {Function} method_func 方法函数
|
|
446
|
+
* @param {Array} params 参数
|
|
447
|
+
* @param {any} result 前置钩子结果
|
|
448
|
+
* @returns {Promise<any>} 主方法结果
|
|
449
|
+
*/
|
|
450
|
+
Mod.prototype._callMain = async function (method_func, params, result) {
|
|
451
|
+
let method_result = await method_func.call(this._mod || this, ...params);
|
|
452
|
+
// 修复:如果主方法返回undefined,则使用前置钩子的结果
|
|
453
|
+
return method_result !== undefined ? method_result : result;
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* 执行后置钩子
|
|
458
|
+
* @param {string} method 方法名
|
|
459
|
+
* @param {Array} params 参数
|
|
460
|
+
* @param {any} result 主方法结果
|
|
461
|
+
* @returns {Promise<any>} 后置钩子结果
|
|
462
|
+
*/
|
|
463
|
+
Mod.prototype._callAfter = async function (method, params, result) {
|
|
464
|
+
let after_method = `${method}After`;
|
|
465
|
+
let after_func = this.getMethod(after_method);
|
|
466
|
+
|
|
467
|
+
if (!after_func) return result;
|
|
468
|
+
|
|
469
|
+
try {
|
|
470
|
+
let after = await after_func.call(this._mod || this, result, ...params);
|
|
471
|
+
return after !== undefined ? after : result;
|
|
472
|
+
} catch (err) {
|
|
473
|
+
this.log('error', `执行后置钩子 ${after_method} 失败: `, err);
|
|
474
|
+
return result;
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
module.exports = {
|
|
479
|
+
Mod
|
|
480
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mm_machine",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.8",
|
|
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": {
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"node": ">=12.0.0"
|
|
28
28
|
},
|
|
29
29
|
"files": [
|
|
30
|
-
"
|
|
30
|
+
"mod.js",
|
|
31
31
|
"index.js",
|
|
32
|
-
"
|
|
32
|
+
"drive.js",
|
|
33
33
|
"README.md"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|