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.
Files changed (4) hide show
  1. package/drive.js +598 -0
  2. package/index.js +10 -10
  3. package/mod.js +480 -0
  4. 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] || 0;
98
- let p2 = o2[sort_key] || 0;
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.exec('load');
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.exec('unload');
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.exec('reload');
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.exec('load'));
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.exec('loadScript'));
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.do('loadScript');
434
+ await mod.call('loadScript');
435
435
  }
436
436
 
437
437
  // 执行方法
438
- let ret = await mod.exec(method, ...params);
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.6",
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
- "base.js",
30
+ "mod.js",
31
31
  "index.js",
32
- "item.js",
32
+ "drive.js",
33
33
  "README.md"
34
34
  ],
35
35
  "dependencies": {