mm_machine 2.7.6 → 2.7.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 (3) hide show
  1. package/drive.js +716 -699
  2. package/index.js +1006 -1000
  3. package/package.json +7 -7
package/drive.js CHANGED
@@ -1,699 +1,716 @@
1
- const prettier = require('@prettier/sync');
2
- require('mm_tpl');
3
- let {
4
- conf
5
- } = require('mm_config');
6
- // const { Word } = require('./ulits/word.js');
7
- const {
8
- Mod
9
- } = require('./mod');
10
-
11
- if (!$.prettyCode) {
12
- $.prettyCode = function (code, options) {
13
- return prettier.format(code, options);
14
- };
15
- }
16
-
17
- /**
18
- * 驱动基础类
19
- * @class
20
- */
21
- class Drive extends Mod {
22
- // 配置项
23
- static config = {
24
- // 名称, 由中英文和下"_"组成, 用于卸载接口 例如: demo
25
- name: '',
26
- // 标题, 介绍作用
27
- title: '',
28
- // 描述, 用于描述该有什么用的
29
- description: '',
30
- // 文件路径, 当调用函数不存在时,会先从文件中加载
31
- main: './index.js',
32
- // 回调函数名 用于决定调用脚本的哪个函数
33
- func_name: 'main',
34
- // 作用域(同时作为检索的后缀名)
35
- scope: ($.val && $.val.scope) ? $.val.scope : 'server',
36
- // 排序
37
- sort: 10,
38
- // 状态, 0表示未启用, 1表示启用
39
- state: 1,
40
- // 显示, 0表示不显示, 1表示显示
41
- show: 0,
42
- // 是否结束,可选,默认false
43
- end: false
44
- };
45
-
46
- /**
47
- * 构造函数
48
- * @param {object} config 配置项
49
- * @param {object} parent 父项
50
- * @class
51
- */
52
- constructor(config, parent) {
53
- super({ ...Drive.config, ...config }, parent);
54
- // 当前配置文件路径
55
- this.config_file = '';
56
-
57
- // 当前脚本文件路径
58
- this.script_file = '';
59
-
60
- // 模式: 1.生产模式 2.热更新模式 3.热重载模式 4.热更新+重载模式 5.重载模式
61
- this.mode = 1;
62
- }
63
- }
64
-
65
- /**
66
- * 获取事件触发器
67
- * @returns {object} 事件触发器
68
- */
69
- Drive.prototype.getEventer = function () {
70
- return $.eventer;
71
- };
72
-
73
- /**
74
- * 配置驱动
75
- */
76
- Drive.prototype._preset = function () {
77
- let file = this.config.main;
78
- if (file) {
79
- this.script_file = file.fullname(this.getDir());
80
- }
81
- };
82
-
83
- /**
84
- * 获取配置
85
- * @param {string} file 配置文件路径
86
- * @returns {object} 配置项
87
- */
88
- Drive.prototype._loadConfig = function (file) {
89
- if (file) {
90
- this.config_file = file;
91
- };
92
- // 加载配置
93
- let config = $.loadJson(this.config_file, (cg) => {
94
- this.setConfig(cg);
95
- return cg;
96
- });
97
- if (!config) {
98
- this.newConfig(this.config_file);
99
- config = $.loadJson(this.config_file, (cg) => {
100
- this.setConfig(cg);
101
- return cg;
102
- });
103
- }
104
- this.setConfig(config);
105
- return config;
106
- };
107
-
108
- /**
109
- * 加载配置
110
- * @param {string} file 配置文件路径
111
- */
112
- Drive.prototype.loadConfig = function (file) {
113
- this._loadConfig(file);
114
- };
115
-
116
- /**
117
- * 设置配置
118
- * @param {object} config 配置对象
119
- * @returns {object} 配置项
120
- */
121
- Drive.prototype.setConfig = function (config) {
122
- if (config) {
123
- // 如果main改变,重置加载状态
124
- if (config.main !== this.config.main) {
125
- this.is_loaded = false;
126
- }
127
-
128
- if (this.config_file) {
129
- // 合并配置并应用配置处理
130
- this.config = conf({
131
- ...this.config,
132
- ...config
133
- }, this.config_file);
134
- }
135
- else {
136
- Object.assign(this.config, config);
137
- }
138
- }
139
- this._preset();
140
- return this.config;
141
- };
142
-
143
- /**
144
- * 获取配置
145
- * @returns {object} 配置项
146
- */
147
- Drive.prototype.getConfig = function () {
148
- return this.config;
149
- };
150
-
151
- /**
152
- * 获取当前文件
153
- * @returns {object} 当前文件对象
154
- */
155
- Drive.prototype.getDir = function () {
156
- let dir = this._dir;
157
- if (!dir) {
158
- if (this.script_file) {
159
- dir = this.script_file.dirname();
160
- this._dir = dir;
161
- }
162
- else if (this.config_file) {
163
- dir = this.config_file.dirname();
164
- this._dir = dir;
165
- }
166
- else if (this.getParent) {
167
- let parent_dir = this.getParent()?.getDir() || '';
168
- if (parent_dir) {
169
- dir = this.config.name.fullname(parent_dir);
170
- }
171
- }
172
- }
173
- return dir;
174
- };
175
-
176
- /**
177
- * 获取模板目录
178
- * @returns {string} 模板目录
179
- */
180
- Drive.prototype._getTplDir = function () {
181
- return this.getParent()?.getTplDir() || '';
182
- };
183
-
184
- /**
185
- * 移除加载的脚本模块
186
- */
187
- Drive.prototype._remove = function () {
188
- let file = this._getScriptFile();
189
- if (!file) return;
190
-
191
- try {
192
- if (this.mode === 3 || this.mode === 4) {
193
- // 移除模块和监听
194
- $.mod.unload(file);
195
- } else {
196
- // 移除模块缓存
197
- let filename = require.resolve(file);
198
- if (require.cache[filename]) {
199
- require.cache[filename] = null;
200
- delete require.cache[filename];
201
- }
202
- }
203
- } catch (err) {
204
- this.log('error', `移除加载的脚本模块失败: `, err);
205
- }
206
- };
207
-
208
- /**
209
- * 卸载脚本
210
- */
211
- Drive.prototype.unloadScript = function () {
212
- this._remove();
213
- this.is_loaded = false;
214
- };
215
-
216
- /**
217
- * 重新加载脚本
218
- * @returns {object | null} 返回加载的模块对象
219
- */
220
- Drive.prototype.reloadScript = function () {
221
- this.unloadScript();
222
- return this.loadScript();
223
- };
224
-
225
- /**
226
- * 卸载之后处理
227
- * @param {boolean} remove 是否删除文件
228
- */
229
- Drive.prototype.unloadCore = async function (remove) {
230
- // 删除脚本
231
- this.unloadScript();
232
- if (remove) {
233
- this.delFile();
234
- }
235
- };
236
-
237
- /**
238
- * 获取脚本文件路径
239
- * @returns {string | null} 完整文件路径
240
- */
241
- Drive.prototype._getScriptFile = function () {
242
- let main = this.config.main || '';
243
- if (!main) return null;
244
-
245
- let filename = main.fullname(this.getDir());
246
- if (!filename.hasFile()) {
247
- this.newScript(filename);
248
- }
249
- return filename;
250
- };
251
-
252
- /**
253
- * 新建脚本文件
254
- * @param {string} file 目标文件路径
255
- */
256
- Drive.prototype.newScript = function (file) {
257
- this._createScriptFile(file);
258
- };
259
-
260
- /**
261
- * 创建脚本
262
- * @param {string} file 文件路径
263
- * @returns {boolean} 创建是否成功
264
- */
265
- Drive.prototype._createScriptFile = function (file) {
266
- var tpl = this._getScriptTpl();
267
- var content = $.tpl.render(tpl, this.getModel('script'));
268
- if (!content) {
269
- return '';
270
- }
271
- let code = this.prettyCode(content, 'js');
272
- file.saveText(code);
273
- return file;
274
- };
275
-
276
- /**
277
- * 获取脚本模板
278
- * @returns {string} 脚本模板内容
279
- */
280
- Drive.prototype._getScriptTpl = function () {
281
- var f = './script.tpl.js'.fullname(this._getTplDir());
282
- if (f.hasFile()) {
283
- return f.loadText();
284
- } else {
285
- return `module.exports = {
286
- /**
287
- * 初始化
288
- */
289
- async _init() {
290
- this.log('debug', \`初始化!\`);
291
- },
292
- /**
293
- * 启动
294
- */
295
- async _start() {
296
- this.log('debug', \`启动!\`);
297
- },
298
- /**
299
- * 停止
300
- */
301
- async _stop() {
302
- this.log('debug', \`停止!\`);
303
- },
304
- /**
305
- * 销毁
306
- */
307
- async _destroy(...args) {
308
- this.log('debug', \`销毁!\`);
309
- },
310
- /**
311
- * 主要逻辑
312
- */
313
- async main(...args) {
314
- // 主要代码写在这
315
- }
316
- }`;
317
- }
318
-
319
- };
320
-
321
- /**
322
- * 加载脚本文件,支持热重载模式
323
- * 在开发模式(mode=3或4)下使用 $.require 进行热重载
324
- * 在生产模式下使用标准的 require 加载
325
- * @returns {object | null} 加载的模块对象
326
- */
327
- Drive.prototype.loadScript = function () {
328
- let file = this._getScriptFile();
329
- if (!file) return null;
330
- if (this.mode === 3 || this.mode === 4) {
331
- // 开发模式:使用 $.require 进行热重载,绑定热更新回调
332
- var cs = $.require(file, (mod) => {
333
- this._setMainMethod(mod);
334
- });
335
- this._setMainMethod(cs);
336
- return cs;
337
- } else {
338
- var cs = require(file);
339
- this._setMainMethod(cs);
340
- return cs;
341
- }
342
- };
343
-
344
- /**
345
- * 设置主方法
346
- * @param {object} mod 模块对象
347
- */
348
- Drive.prototype._setMainMethod = function (mod) {
349
- if (!mod) return;
350
- let name = this.config.func_name || 'main';
351
- if (name && mod[name]) {
352
- this.main = mod[name];
353
- }
354
- for (let key in mod) {
355
- let obj = mod[key];
356
- if (typeof obj === 'function') {
357
- this.setMethod(key, obj);
358
- }
359
- else {
360
- this.setData(key, obj);
361
- }
362
- }
363
- this.is_loaded = true;
364
- };
365
-
366
- /**
367
- * 获取配置文件作用域
368
- * @param {object} file 文件对象
369
- * @returns {string} 配置文件作用域
370
- */
371
- Drive.prototype._getScope = function (file) {
372
- return file.replace(/\\/g, '/').between('app/', '/');
373
- };
374
-
375
- /**
376
- * 新建配置文件
377
- * @param {string} file 目标文件路径
378
- */
379
- Drive.prototype.newConfig = function (file) {
380
- if (!this.config.name) {
381
- this.config.name = this._getName(file);
382
- this.config.scope = this._getScope(file);
383
- }
384
- this._createConfigFile(file);
385
- };
386
-
387
- /**
388
- * 预试代码
389
- * @param {string} code 要预试的代码
390
- * @param {string} type 预试类型,可选"json"或"js"
391
- * @returns {string} 预试后的代码字符串
392
- */
393
- Drive.prototype.prettyCode = function (code, type = 'json5') {
394
- let options = {
395
- tabWidth: 2
396
- };
397
- switch (type) {
398
- case 'json5':
399
- options.parser = 'json5';
400
- break;
401
- case 'js':
402
- case 'javascript':
403
- options.parser = 'babel';
404
- options.semi = true; // 在语句末尾添加分号
405
- options.singleQuote = true; // 使用单引号
406
- break;
407
- default:
408
- options.parser = 'json';
409
- break;
410
- }
411
- let new_code = $.prettyCode(code, options);
412
- return new_code;
413
- };
414
-
415
- /**
416
- * 获取模块模型
417
- * @param {string} type 模型类型,可选"config"或"script"
418
- * @returns {object} 模块模型
419
- */
420
- Drive.prototype.getModel = function (type) {
421
- return this.config;
422
- };
423
-
424
- /**
425
- * 创建配置文件
426
- * @param {string} file 目标文件路径
427
- * @param {object} model 模块模型
428
- * @returns {string} 配置文件内容
429
- */
430
- Drive.prototype._createConfigFile = function (file, model) {
431
- var tpl = this._getConfigTpl();
432
- var content = $.tpl.render(tpl, model || this.getModel('config'));
433
- let code = this.prettyCode(content, 'json');
434
- file.saveText(code);
435
- return file;
436
- };
437
-
438
- /**
439
- * 创建配置文件
440
- * @param {string} file 目标文件路径
441
- * @param {object} model 模块模型
442
- * @returns {string} 配置文件内容
443
- */
444
- Drive.prototype.createConfigFile = function (file, model) {
445
- file.addDir();
446
- return this._createConfigFile(file, model);
447
- };
448
-
449
- /**
450
- * 获取配置模板
451
- * @returns {string} 配置模板内容
452
- */
453
- Drive.prototype._getConfigTpl = function () {
454
- var f = './config.tpl.json'.fullname(this._getTplDir());
455
- if (f.hasFile()) {
456
- return f.loadText();
457
- } else {
458
- return `{
459
- // 模块名称,必填
460
- "name": "\${name}",
461
- // 模块标题,可选,默认"示例标题"
462
- "title": "\${title || '示例标题'}",
463
- // 模块描述,可选,默认"示例描述"
464
- "description": "\${description || '示例描述'}",
465
- // 主脚本文件路径,可选,默认"./index.js"
466
- "main": "\${main || './index.js'}",
467
- // 模块作用域,可选,默认"server"
468
- "scope": "\${scope || 'server'}",
469
- // 模块状态,可选,默认1
470
- "state": \${state || 1},
471
- // 模块排序,可选,默认100
472
- "sort": \${sort || 100},
473
- // 是否结束,可选,默认false
474
- "end": \${end || false}
475
- }`;
476
- }
477
- };
478
-
479
- /**
480
- * 加载配置文件
481
- * @param {string} file 文件路径
482
- * @param {string} name 配置项名称
483
- * @returns {object | null} 配置对象
484
- */
485
- Drive.prototype.loadFile = function (file, name) {
486
- try {
487
- let filename = file.fullname(this.getDir());
488
- let text = filename.loadText();
489
-
490
- // 如果文件不存在,创建新的配置文件
491
- if (!text) {
492
- this.newConfig(filename);
493
- text = filename.loadText();
494
- }
495
-
496
- if (!text) return null;
497
-
498
- let config = this._loadHotReload(filename);
499
- let final_config = this._findConfigByName(config, name);
500
- return final_config;
501
- } catch (err) {
502
- this.log('error', `加载配置文件失败: `, err);
503
- return null;
504
- }
505
- };
506
-
507
- /**
508
- * 根据模式加载配置(支持热更新)
509
- * @param {string} file 完整文件路径
510
- * @returns {object | Array | null} 配置对象
511
- */
512
- Drive.prototype._loadHotReload = function (file) {
513
- if (this.mode === 2 || this.mode === 3 || this.mode === 4) {
514
- return $.loadJson(file, this._handleHotReload.bind(this));
515
- }
516
- return file.loadJson();
517
- };
518
-
519
- /**
520
- * 根据名称在配置数组中查找配置项
521
- * @param {object | Array} config 配置对象或数组
522
- * @param {string} name 配置项名称
523
- * @returns {object | null} 匹配的配置项
524
- */
525
- Drive.prototype._findConfigByName = function (config, name) {
526
- if (!name || !Array.isArray(config)) return config;
527
- return config.find((Drive) => Drive.name === name) || null;
528
- };
529
-
530
- /**
531
- * 根据模式决定是否重新加载脚本
532
- */
533
- Drive.prototype._reloadIfNeeded = function () {
534
- if (this.mode === 3 || this.mode === 4) {
535
- this.reloadScript();
536
- }
537
- };
538
-
539
- /**
540
- * 删除目录
541
- */
542
- Drive.prototype.delDir = function () {
543
- let main = this.config.main;
544
- if (main && $.dir && $.dir.del) {
545
- $.dir.del(this.dir);
546
- }
547
- };
548
-
549
- /**
550
- * 删除配置和脚本文件
551
- * @returns {string | null} 错误消息,如果没有错误则返回null
552
- */
553
- Drive.prototype.delFile = function () {
554
- let name = this.config.name;
555
- let file = this.script_file;
556
-
557
- let error_message = null;
558
- try {
559
- if (!file || !file.hasFile) return null;
560
-
561
- if (!file.hasFile()) {
562
- return '配置文件不存在';
563
- }
564
-
565
- error_message = this._delConfigFile(file, name);
566
- } catch (err) {
567
- this.log('error', `删除文件失败: `, err);
568
- error_message = `删除失败: ${err.message}`;
569
- }
570
- return error_message;
571
- };
572
-
573
- /**
574
- * 删除配置文件
575
- * @param {object} file 文件对象
576
- * @param {string} name 配置名称
577
- * @returns {string | null} 错误消息
578
- */
579
- Drive.prototype._delConfigFile = function (file, name) {
580
- let text = file.loadText();
581
- if (!text) {
582
- this.delDir();
583
- return null;
584
- }
585
-
586
- let config = text.toJson();
587
- if (Array.isArray(config)) {
588
- return this._delArray(file, config, name);
589
- }
590
-
591
- this.delDir();
592
- return null;
593
- };
594
-
595
- /**
596
- * 从数组配置中删除指定项
597
- * @param {object} file 文件对象
598
- * @param {Array} config 配置数组
599
- * @param {string} name 配置名称
600
- * @returns {string | null} 错误消息
601
- */
602
- Drive.prototype._delArray = function (file, config, name) {
603
- let index = config.findIndex((Drive) => Drive.name === name);
604
- if (index === -1) return null;
605
-
606
- this.delDir();
607
- config.splice(index, 1);
608
-
609
- if (config.length > 0) {
610
- file.saveText(JSON.stringify(config, null, 4));
611
- } else if (file.delFile) {
612
- file.delFile();
613
- }
614
-
615
- return null;
616
- };
617
-
618
- /**
619
- * 获取配置文件名称
620
- * @param {object} file 文件对象
621
- * @returns {string} 配置文件名称
622
- */
623
- Drive.prototype._getName = function (file) {
624
- let name = file.dirname().basename();
625
- return name;
626
- // // 提取文件名作为模块名
627
- // var word = new Word();
628
- // // 将文件名改为驼峰命名法
629
- // return word.toCamelCase(name);
630
- };
631
-
632
- /**
633
- * 加载处理
634
- */
635
- Drive.prototype._loadCore = function () {
636
- try {
637
- let module = this.loadScript();
638
- if (module) {
639
- this.is_loaded = true;
640
- }
641
- } catch (err) {
642
- this.log('error', `加载前处理失败: `, err);
643
- this.is_loaded = false;
644
- }
645
- // this.loadConfig();
646
- // this.loadScript();
647
- };
648
-
649
- /**
650
- * 保存配置
651
- */
652
- Drive.prototype.save = function () {
653
- try {
654
- if (!this.script_file) return;
655
-
656
- let file = this.script_file.fullname(this.getDir());
657
- let text = file.loadText();
658
-
659
- if (text && text.trim().startsWith('[')) {
660
- // 数组格式配置
661
- let config = text.toJson();
662
- if (Array.isArray(config)) {
663
- // 查找并更新现有配置项
664
- let index = config.findIndex((Drive) => Drive.name === this.config.name);
665
- if (index !== -1) {
666
- config[index] = this.config;
667
- } else {
668
- // 如果不存在,添加新配置项
669
- config.push(this.config);
670
- }
671
- file.saveText(JSON.stringify(config, null, 2));
672
- return;
673
- }
674
- }
675
-
676
- // 单对象格式配置,直接保存
677
- file.saveText(JSON.stringify(this.config, null, 2));
678
- } catch (err) {
679
- this.log('error', `保存配置失败: `, err);
680
- }
681
- };
682
-
683
- /**
684
- * 删除项目
685
- */
686
- Drive.prototype.remove = function () {
687
- let dir = this.getDir();
688
- if (dir) {
689
- dir.delDir();
690
- }
691
- return dir;
692
- };
693
-
694
- /**
695
- * @module 导出Drive类
696
- */
697
- module.exports = {
698
- Drive
699
- };
1
+ const prettier = require('@prettier/sync');
2
+ require('mm_tpl');
3
+ let { conf } = require('mm_config');
4
+ // const { Word } = require('./ulits/word.js');
5
+ const { Mod } = require('./mod');
6
+
7
+ if (!$.prettyCode) {
8
+ $.prettyCode = function (code, options) {
9
+ return prettier.format(code, options);
10
+ };
11
+ }
12
+
13
+ /**
14
+ * 驱动基础类
15
+ * @class
16
+ */
17
+ class Drive extends Mod {
18
+ // 配置项
19
+ static config = {
20
+ // 名称, 由中英文和下"_"组成, 用于卸载接口 例如: demo
21
+ name: '',
22
+ // 标题, 介绍作用
23
+ title: '',
24
+ // 描述, 用于描述该有什么用的
25
+ description: '',
26
+ // 文件路径, 当调用函数不存在时,会先从文件中加载
27
+ main: './index.js',
28
+ // 回调函数名 用于决定调用脚本的哪个函数
29
+ func_name: 'main',
30
+ // 作用域(同时作为检索的后缀名)
31
+ scope: $.val && $.val.scope ? $.val.scope : 'server',
32
+ // 排序
33
+ sort: 10,
34
+ // 状态, 0表示未启用, 1表示启用
35
+ state: 1,
36
+ // 显示, 0表示不显示, 1表示显示
37
+ show: 0,
38
+ // 是否结束,可选,默认false
39
+ end: false
40
+ };
41
+
42
+ /**
43
+ * 构造函数
44
+ * @param {object} config 配置项
45
+ * @param {object} parent 父项
46
+ * @class
47
+ */
48
+ constructor(config, parent) {
49
+ super({ ...Drive.config, ...config }, parent);
50
+ // 当前配置文件路径
51
+ this.config_file = '';
52
+
53
+ // 当前脚本文件路径
54
+ this.script_file = '';
55
+
56
+ // 模式: 1.生产模式 2.热更新模式 3.热重载模式 4.热更新+重载模式 5.重载模式
57
+ this.mode = 1;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * 获取事件触发器
63
+ * @returns {object} 事件触发器
64
+ */
65
+ Drive.prototype.getEventer = function () {
66
+ return $.eventer;
67
+ };
68
+
69
+ /**
70
+ * 配置驱动
71
+ */
72
+ Drive.prototype._preset = function () {
73
+ let file = this.config.main;
74
+ if (file) {
75
+ this.script_file = file.fullname(this.getDir());
76
+ }
77
+ };
78
+
79
+ /**
80
+ * 获取配置
81
+ * @param {string} file 配置文件路径
82
+ * @returns {object} 配置项
83
+ */
84
+ Drive.prototype._loadConfig = function (file) {
85
+ if (file) {
86
+ this.config_file = file;
87
+ }
88
+ // 加载配置
89
+ let config = $.loadJson(this.config_file, (cg) => {
90
+ this.setConfig(cg);
91
+ return cg;
92
+ });
93
+ if (!config) {
94
+ this.newConfig(this.config_file);
95
+ config = $.loadJson(this.config_file, (cg) => {
96
+ this.setConfig(cg);
97
+ return cg;
98
+ });
99
+ }
100
+ this.setConfig(config);
101
+ return config;
102
+ };
103
+
104
+ /**
105
+ * 加载配置
106
+ * @param {string} file 配置文件路径
107
+ */
108
+ Drive.prototype.loadConfig = function (file) {
109
+ this._loadConfig(file);
110
+ };
111
+
112
+ /**
113
+ * 设置配置
114
+ * @param {object} config 配置对象
115
+ * @returns {object} 配置项
116
+ */
117
+ Drive.prototype.setConfig = function (config) {
118
+ if (config) {
119
+ // 如果main改变,重置加载状态
120
+ if (config.main !== this.config.main) {
121
+ this.is_loaded = false;
122
+ }
123
+
124
+ if (this.config_file) {
125
+ // 合并配置并应用配置处理
126
+ this.config = conf(
127
+ {
128
+ ...this.config,
129
+ ...config
130
+ },
131
+ this.config_file
132
+ );
133
+ } else {
134
+ Object.assign(this.config, config);
135
+ }
136
+ }
137
+ this._preset();
138
+ return this.config;
139
+ };
140
+
141
+ /**
142
+ * 获取配置
143
+ * @returns {object} 配置项
144
+ */
145
+ Drive.prototype.getConfig = function () {
146
+ return this.config;
147
+ };
148
+
149
+ /**
150
+ * 获取当前文件
151
+ * @returns {object} 当前文件对象
152
+ */
153
+ Drive.prototype.getDir = function () {
154
+ let dir = this._dir;
155
+ if (!dir) {
156
+ if (this.script_file) {
157
+ dir = this.script_file.dirname();
158
+ this._dir = dir;
159
+ } else if (this.config_file) {
160
+ dir = this.config_file.dirname();
161
+ this._dir = dir;
162
+ } else if (this.getParent) {
163
+ let parent_dir = this.getParent()?.getDir() || '';
164
+ if (parent_dir) {
165
+ dir = this.config.name.fullname(parent_dir);
166
+ }
167
+ }
168
+ }
169
+ return dir;
170
+ };
171
+
172
+ /**
173
+ * 获取模板目录
174
+ * @returns {string} 模板目录
175
+ */
176
+ Drive.prototype._getTplDir = function () {
177
+ return this.getParent()?.getTplDir() || '';
178
+ };
179
+
180
+ /**
181
+ * 移除加载的脚本模块
182
+ */
183
+ Drive.prototype._remove = function () {
184
+ let file = this._getScriptFile();
185
+ if (!file) return;
186
+
187
+ try {
188
+ if (this.mode === 3 || this.mode === 4) {
189
+ // 移除模块和监听
190
+ $.mod.unload(file);
191
+ } else {
192
+ // 移除模块缓存
193
+ let filename = require.resolve(file);
194
+ if (require.cache[filename]) {
195
+ require.cache[filename] = null;
196
+ delete require.cache[filename];
197
+ }
198
+ }
199
+ } catch (err) {
200
+ this.log('error', `移除加载的脚本模块失败: `, err);
201
+ }
202
+ };
203
+
204
+ /**
205
+ * 卸载脚本
206
+ */
207
+ Drive.prototype.unloadScript = function () {
208
+ this._remove();
209
+ this._script_keys = null;
210
+ this.is_loaded = false;
211
+ };
212
+
213
+ /**
214
+ * 重新加载脚本
215
+ * @returns {object | null} 返回加载的模块对象
216
+ */
217
+ Drive.prototype.reloadScript = function () {
218
+ this.unloadScript();
219
+ return this.loadScript();
220
+ };
221
+
222
+ /**
223
+ * 卸载之后处理
224
+ * @param {boolean} remove 是否删除文件
225
+ */
226
+ Drive.prototype.unloadCore = async function (remove) {
227
+ // 删除脚本
228
+ this.unloadScript();
229
+ if (remove) {
230
+ this.delFile();
231
+ }
232
+ };
233
+
234
+ /**
235
+ * 获取脚本文件路径
236
+ * @returns {string | null} 完整文件路径
237
+ */
238
+ Drive.prototype._getScriptFile = function () {
239
+ let main = this.config.main || '';
240
+ if (!main) return null;
241
+
242
+ let filename = main.fullname(this.getDir());
243
+ if (!filename.hasFile()) {
244
+ this.newScript(filename);
245
+ }
246
+ return filename;
247
+ };
248
+
249
+ /**
250
+ * 新建脚本文件
251
+ * @param {string} file 目标文件路径
252
+ */
253
+ Drive.prototype.newScript = function (file) {
254
+ this._createScriptFile(file);
255
+ };
256
+
257
+ /**
258
+ * 创建脚本
259
+ * @param {string} file 文件路径
260
+ * @returns {boolean} 创建是否成功
261
+ */
262
+ Drive.prototype._createScriptFile = function (file) {
263
+ var tpl = this._getScriptTpl();
264
+ var content = $.tpl.render(tpl, this.getModel('script'));
265
+ if (!content) {
266
+ return '';
267
+ }
268
+ let code = this.prettyCode(content, 'js');
269
+ file.saveText(code);
270
+ return file;
271
+ };
272
+
273
+ /**
274
+ * 获取脚本模板
275
+ * @returns {string} 脚本模板内容
276
+ */
277
+ Drive.prototype._getScriptTpl = function () {
278
+ var f = './script.tpl.js'.fullname(this._getTplDir());
279
+ if (f.hasFile()) {
280
+ return f.loadText();
281
+ } else {
282
+ return `module.exports = {
283
+ /**
284
+ * 初始化
285
+ */
286
+ async _init() {
287
+ this.log('debug', \`初始化!\`);
288
+ },
289
+ /**
290
+ * 启动
291
+ */
292
+ async _start() {
293
+ this.log('debug', \`启动!\`);
294
+ },
295
+ /**
296
+ * 停止
297
+ */
298
+ async _stop() {
299
+ this.log('debug', \`停止!\`);
300
+ },
301
+ /**
302
+ * 销毁
303
+ */
304
+ async _destroy(...args) {
305
+ this.log('debug', \`销毁!\`);
306
+ },
307
+ /**
308
+ * 主要逻辑
309
+ */
310
+ async main(...args) {
311
+ // 主要代码写在这
312
+ }
313
+ }`;
314
+ }
315
+ };
316
+
317
+ /**
318
+ * 加载脚本文件,支持热重载模式
319
+ * 在开发模式(mode=3或4)下使用 $.require 进行热重载
320
+ * 在生产模式下使用标准的 require 加载
321
+ * @returns {object | null} 加载的模块对象
322
+ */
323
+ Drive.prototype.loadScript = function () {
324
+ let file = this._getScriptFile();
325
+ if (!file) return null;
326
+ if (this.mode === 3 || this.mode === 4) {
327
+ // 开发模式:使用 $.require 进行热重载,绑定热更新回调
328
+ var cs = $.require(file, (mod) => {
329
+ this._setMainMethod(mod);
330
+ });
331
+ this._setMainMethod(cs);
332
+ return cs;
333
+ } else {
334
+ var cs = require(file);
335
+ this._setMainMethod(cs);
336
+ return cs;
337
+ }
338
+ };
339
+
340
+ /**
341
+ * 设置主方法
342
+ * @param {object} mod 模块对象
343
+ */
344
+ Drive.prototype._setMainMethod = function (mod) {
345
+ if (!mod) return;
346
+ var name = this.config.func_name || 'main';
347
+ if (name && mod[name]) {
348
+ this.main = mod[name];
349
+ }
350
+ var new_keys = Object.keys(mod);
351
+ // 热重载时如果脚本未变化则跳过重建
352
+ if (this._script_keys && this._script_keys.length === new_keys.length) {
353
+ var same = true;
354
+ for (var i = 0; i < new_keys.length; i++) {
355
+ if (this._script_keys[i] !== new_keys[i]) {
356
+ same = false;
357
+ break;
358
+ }
359
+ }
360
+ if (same) {
361
+ this.is_loaded = true;
362
+ return;
363
+ }
364
+ }
365
+ this._script_keys = new_keys;
366
+ for (var key in mod) {
367
+ var obj = mod[key];
368
+ if (typeof obj === 'function') {
369
+ this.setMethod(key, obj);
370
+ } else {
371
+ this.setData(key, obj);
372
+ }
373
+ }
374
+ this.is_loaded = true;
375
+ };
376
+
377
+ /**
378
+ * 获取配置文件作用域
379
+ * @param {object} file 文件对象
380
+ * @returns {string} 配置文件作用域
381
+ */
382
+ Drive.prototype._getScope = function (file) {
383
+ return file.replace(/\\/g, '/').between('app/', '/');
384
+ };
385
+
386
+ /**
387
+ * 新建配置文件
388
+ * @param {string} file 目标文件路径
389
+ */
390
+ Drive.prototype.newConfig = function (file) {
391
+ if (!this.config.name) {
392
+ this.config.name = this._getName(file);
393
+ this.config.scope = this._getScope(file);
394
+ }
395
+ this._createConfigFile(file);
396
+ };
397
+
398
+ /**
399
+ * 预试代码
400
+ * @param {string} code 要预试的代码
401
+ * @param {string} type 预试类型,可选"json"或"js"
402
+ * @returns {string} 预试后的代码字符串
403
+ */
404
+ Drive.prototype.prettyCode = function (code, type = 'json5') {
405
+ let options = {
406
+ tabWidth: 2
407
+ };
408
+ switch (type) {
409
+ case 'json5':
410
+ options.parser = 'json5';
411
+ break;
412
+ case 'js':
413
+ case 'javascript':
414
+ options.parser = 'babel';
415
+ options.semi = true; // 在语句末尾添加分号
416
+ options.singleQuote = true; // 使用单引号
417
+ break;
418
+ default:
419
+ options.parser = 'json';
420
+ break;
421
+ }
422
+ let new_code = $.prettyCode(code, options);
423
+ return new_code;
424
+ };
425
+
426
+ /**
427
+ * 获取模块模型
428
+ * @param {string} type 模型类型,可选"config"或"script"
429
+ * @returns {object} 模块模型
430
+ */
431
+ Drive.prototype.getModel = function (type) {
432
+ return this.config;
433
+ };
434
+
435
+ /**
436
+ * 创建配置文件
437
+ * @param {string} file 目标文件路径
438
+ * @param {object} model 模块模型
439
+ * @returns {string} 配置文件内容
440
+ */
441
+ Drive.prototype._createConfigFile = function (file, model) {
442
+ var tpl = this._getConfigTpl();
443
+ var content = $.tpl.render(tpl, model || this.getModel('config'));
444
+ let code = this.prettyCode(content, 'json');
445
+ file.saveText(code);
446
+ return file;
447
+ };
448
+
449
+ /**
450
+ * 创建配置文件
451
+ * @param {string} file 目标文件路径
452
+ * @param {object} model 模块模型
453
+ * @returns {string} 配置文件内容
454
+ */
455
+ Drive.prototype.createConfigFile = function (file, model) {
456
+ file.addDir();
457
+ return this._createConfigFile(file, model);
458
+ };
459
+
460
+ /**
461
+ * 获取配置模板
462
+ * @returns {string} 配置模板内容
463
+ */
464
+ Drive.prototype._getConfigTpl = function () {
465
+ var f = './config.tpl.json'.fullname(this._getTplDir());
466
+ if (f.hasFile()) {
467
+ return f.loadText();
468
+ } else {
469
+ return `{
470
+ // 模块名称,必填
471
+ "name": "\${name}",
472
+ // 模块标题,可选,默认"示例标题"
473
+ "title": "\${title || '示例标题'}",
474
+ // 模块描述,可选,默认"示例描述"
475
+ "description": "\${description || '示例描述'}",
476
+ // 主脚本文件路径,可选,默认"./index.js"
477
+ "main": "\${main || './index.js'}",
478
+ // 模块作用域,可选,默认"server"
479
+ "scope": "\${scope || 'server'}",
480
+ // 模块状态,可选,默认1
481
+ "state": \${state || 1},
482
+ // 模块排序,可选,默认100
483
+ "sort": \${sort || 100},
484
+ // 是否结束,可选,默认false
485
+ "end": \${end || false}
486
+ }`;
487
+ }
488
+ };
489
+
490
+ /**
491
+ * 加载配置文件
492
+ * @param {string} file 文件路径
493
+ * @param {string} name 配置项名称
494
+ * @returns {object | null} 配置对象
495
+ */
496
+ Drive.prototype.loadFile = function (file, name) {
497
+ try {
498
+ let filename = file.fullname(this.getDir());
499
+ let text = filename.loadText();
500
+
501
+ // 如果文件不存在,创建新的配置文件
502
+ if (!text) {
503
+ this.newConfig(filename);
504
+ text = filename.loadText();
505
+ }
506
+
507
+ if (!text) return null;
508
+
509
+ let config = this._loadHotReload(filename);
510
+ let final_config = this._findConfigByName(config, name);
511
+ return final_config;
512
+ } catch (err) {
513
+ this.log('error', `加载配置文件失败: `, err);
514
+ return null;
515
+ }
516
+ };
517
+
518
+ /**
519
+ * 根据模式加载配置(支持热更新)
520
+ * @param {string} file 完整文件路径
521
+ * @returns {object | Array | null} 配置对象
522
+ */
523
+ Drive.prototype._loadHotReload = function (file) {
524
+ if (this.mode === 2 || this.mode === 3 || this.mode === 4) {
525
+ return $.loadJson(file, this._handleHotReload.bind(this));
526
+ }
527
+ return file.loadJson();
528
+ };
529
+
530
+ /**
531
+ * 根据名称在配置数组中查找配置项
532
+ * @param {object | Array} config 配置对象或数组
533
+ * @param {string} name 配置项名称
534
+ * @returns {object | null} 匹配的配置项
535
+ */
536
+ Drive.prototype._findConfigByName = function (config, name) {
537
+ if (!name || !Array.isArray(config)) return config;
538
+ return config.find((Drive) => Drive.name === name) || null;
539
+ };
540
+
541
+ /**
542
+ * 根据模式决定是否重新加载脚本
543
+ */
544
+ Drive.prototype._reloadIfNeeded = function () {
545
+ if (this.mode === 3 || this.mode === 4) {
546
+ this.reloadScript();
547
+ }
548
+ };
549
+
550
+ /**
551
+ * 删除目录
552
+ */
553
+ Drive.prototype.delDir = function () {
554
+ let main = this.config.main;
555
+ if (main && $.dir && $.dir.del) {
556
+ $.dir.del(this.dir);
557
+ }
558
+ };
559
+
560
+ /**
561
+ * 删除配置和脚本文件
562
+ * @returns {string | null} 错误消息,如果没有错误则返回null
563
+ */
564
+ Drive.prototype.delFile = function () {
565
+ let name = this.config.name;
566
+ let file = this.script_file;
567
+
568
+ let error_message = null;
569
+ try {
570
+ if (!file || !file.hasFile) return null;
571
+
572
+ if (!file.hasFile()) {
573
+ return '配置文件不存在';
574
+ }
575
+
576
+ error_message = this._delConfigFile(file, name);
577
+ } catch (err) {
578
+ this.log('error', `删除文件失败: `, err);
579
+ error_message = `删除失败: ${err.message}`;
580
+ }
581
+ return error_message;
582
+ };
583
+
584
+ /**
585
+ * 删除配置文件
586
+ * @param {object} file 文件对象
587
+ * @param {string} name 配置名称
588
+ * @returns {string | null} 错误消息
589
+ */
590
+ Drive.prototype._delConfigFile = function (file, name) {
591
+ let text = file.loadText();
592
+ if (!text) {
593
+ this.delDir();
594
+ return null;
595
+ }
596
+
597
+ let config = text.toJson();
598
+ if (Array.isArray(config)) {
599
+ return this._delArray(file, config, name);
600
+ }
601
+
602
+ this.delDir();
603
+ return null;
604
+ };
605
+
606
+ /**
607
+ * 从数组配置中删除指定项
608
+ * @param {object} file 文件对象
609
+ * @param {Array} config 配置数组
610
+ * @param {string} name 配置名称
611
+ * @returns {string | null} 错误消息
612
+ */
613
+ Drive.prototype._delArray = function (file, config, name) {
614
+ let index = config.findIndex((Drive) => Drive.name === name);
615
+ if (index === -1) return null;
616
+
617
+ this.delDir();
618
+ config.splice(index, 1);
619
+
620
+ if (config.length > 0) {
621
+ file.saveText(JSON.stringify(config, null, 4));
622
+ } else if (file.delFile) {
623
+ file.delFile();
624
+ }
625
+
626
+ return null;
627
+ };
628
+
629
+ /**
630
+ * 获取配置文件名称
631
+ * @param {object} file 文件对象
632
+ * @returns {string} 配置文件名称
633
+ */
634
+ Drive.prototype._getName = function (file) {
635
+ let name = file.dirname().basename();
636
+ return name;
637
+ // // 提取文件名作为模块名
638
+ // var word = new Word();
639
+ // // 将文件名改为驼峰命名法
640
+ // return word.toCamelCase(name);
641
+ };
642
+
643
+ /**
644
+ * 加载前处理
645
+ */
646
+ Drive.prototype.loadBefore = function () {
647
+ try {
648
+ let module = this.loadScript();
649
+ if (module) {
650
+ this.is_loaded = true;
651
+ }
652
+ } catch (err) {
653
+ this.log('error', `加载前处理失败: `, err);
654
+ this.is_loaded = false;
655
+ }
656
+ };
657
+
658
+ /**
659
+ * 加载处理
660
+ */
661
+ Drive.prototype._loadCore = function () {
662
+ // this.loadConfig();
663
+ // this.loadScript();
664
+ };
665
+
666
+ /**
667
+ * 保存配置
668
+ */
669
+ Drive.prototype.save = function () {
670
+ try {
671
+ if (!this.script_file) return;
672
+
673
+ let file = this.script_file.fullname(this.getDir());
674
+ let text = file.loadText();
675
+
676
+ if (text && text.trim().startsWith('[')) {
677
+ // 数组格式配置
678
+ let config = text.toJson();
679
+ if (Array.isArray(config)) {
680
+ // 查找并更新现有配置项
681
+ let index = config.findIndex((Drive) => Drive.name === this.config.name);
682
+ if (index !== -1) {
683
+ config[index] = this.config;
684
+ } else {
685
+ // 如果不存在,添加新配置项
686
+ config.push(this.config);
687
+ }
688
+ file.saveText(JSON.stringify(config, null, 2));
689
+ return;
690
+ }
691
+ }
692
+
693
+ // 单对象格式配置,直接保存
694
+ file.saveText(JSON.stringify(this.config, null, 2));
695
+ } catch (err) {
696
+ this.log('error', `保存配置失败: `, err);
697
+ }
698
+ };
699
+
700
+ /**
701
+ * 删除项目
702
+ */
703
+ Drive.prototype.remove = function () {
704
+ let dir = this.getDir();
705
+ if (dir) {
706
+ dir.delDir();
707
+ }
708
+ return dir;
709
+ };
710
+
711
+ /**
712
+ * @module 导出Drive类
713
+ */
714
+ module.exports = {
715
+ Drive
716
+ };