mm_machine 2.9.3 → 2.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/drive.js +668 -490
- package/index.js +1141 -921
- package/mod.js +742 -711
- package/package.json +2 -2
package/drive.js
CHANGED
|
@@ -1,377 +1,394 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return prettier.format(code, options);
|
|
10
|
-
};
|
|
11
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Drive 驱动类
|
|
3
|
+
* 提供 JSON 配置文件加载、JS 脚本文件热重载、模板文件生成等能力
|
|
4
|
+
* @class Drive
|
|
5
|
+
* @augments Mod
|
|
6
|
+
*/
|
|
7
|
+
var path = require('path');
|
|
8
|
+
var { Mod } = require('./mod');
|
|
12
9
|
|
|
13
10
|
/**
|
|
14
|
-
*
|
|
15
|
-
* @class
|
|
11
|
+
* 静态配置默认值
|
|
16
12
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
};
|
|
13
|
+
var defaultConfig = {
|
|
14
|
+
name: '',
|
|
15
|
+
title: '',
|
|
16
|
+
description: '',
|
|
17
|
+
main: '',
|
|
18
|
+
func_name: 'main',
|
|
19
|
+
scope: 0,
|
|
20
|
+
sort: 100,
|
|
21
|
+
mode: 3,
|
|
22
|
+
state: 1
|
|
23
|
+
};
|
|
41
24
|
|
|
25
|
+
class Drive extends Mod {
|
|
42
26
|
/**
|
|
43
27
|
* 构造函数
|
|
44
|
-
* @param {object} config
|
|
45
|
-
* @param {object} parent
|
|
46
|
-
* @class
|
|
28
|
+
* @param {object} config 配置参数
|
|
29
|
+
* @param {object} [parent] 父对象
|
|
47
30
|
*/
|
|
48
31
|
constructor(config, parent) {
|
|
49
|
-
super({ ...
|
|
50
|
-
|
|
32
|
+
super({ ...defaultConfig, ...config, parent: parent });
|
|
33
|
+
this._config = {};
|
|
34
|
+
this._script = '';
|
|
35
|
+
this.is_loaded = false;
|
|
36
|
+
this._main_func = null;
|
|
37
|
+
this._watch = null;
|
|
38
|
+
this._notify_timer = null;
|
|
39
|
+
this._script_keys = null;
|
|
51
40
|
this.config_file = '';
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this.script_file = '';
|
|
55
|
-
|
|
56
|
-
// 模式: 1.生产模式 2.热更新模式 3.热重载模式 4.热更新+重载模式 5.重载模式
|
|
57
|
-
this.mode = 1;
|
|
41
|
+
this.config = { ...defaultConfig, ...config };
|
|
42
|
+
this.mode = this.config.mode;
|
|
58
43
|
}
|
|
59
44
|
}
|
|
60
45
|
|
|
46
|
+
// ==================== 事件管理 ====================
|
|
47
|
+
|
|
61
48
|
/**
|
|
62
49
|
* 获取事件触发器
|
|
63
50
|
* @returns {object} 事件触发器
|
|
64
51
|
*/
|
|
65
52
|
Drive.prototype.getEventer = function () {
|
|
66
|
-
return $.eventer;
|
|
53
|
+
return typeof $.eventer !== 'undefined' ? $.eventer : this;
|
|
67
54
|
};
|
|
68
55
|
|
|
69
56
|
/**
|
|
70
|
-
*
|
|
57
|
+
* 获取配置
|
|
58
|
+
* @returns {object} 配置对象
|
|
71
59
|
*/
|
|
72
|
-
Drive.prototype.
|
|
73
|
-
|
|
74
|
-
if (file) {
|
|
75
|
-
this.script_file = file.fullname(this.getDir());
|
|
76
|
-
}
|
|
60
|
+
Drive.prototype.getConfig = function () {
|
|
61
|
+
return this.config;
|
|
77
62
|
};
|
|
78
63
|
|
|
79
64
|
/**
|
|
80
|
-
*
|
|
81
|
-
* @
|
|
82
|
-
* @returns {object} 配置项
|
|
65
|
+
* 获取模板目录
|
|
66
|
+
* @returns {string} 模板目录
|
|
83
67
|
*/
|
|
84
|
-
Drive.prototype.
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
68
|
+
Drive.prototype.getTplDir = function () {
|
|
69
|
+
if (!this.getParent) return '';
|
|
70
|
+
var parent = this.getParent();
|
|
71
|
+
return parent ? parent.getTplDir ? parent.getTplDir() : '' : '';
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// ==================== 目录管理 ====================
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 获取模块目录
|
|
78
|
+
* 优先级:脚本文件目录 > 配置文件目录 > 父级目录 > 按名称拼接
|
|
79
|
+
* @returns {string} 目录路径
|
|
80
|
+
*/
|
|
81
|
+
Drive.prototype.getDir = function () {
|
|
82
|
+
if (this._dir) {
|
|
83
|
+
return this._dir;
|
|
84
|
+
}
|
|
85
|
+
if (this._script) {
|
|
86
|
+
this._dir = path.dirname(this._script);
|
|
87
|
+
} else if (this.config_file) {
|
|
88
|
+
this._dir = path.dirname(this.config_file);
|
|
89
|
+
} else if (this.getParent && this.getParent()) {
|
|
90
|
+
var parent_dir = this.getParent().getDir ? this.getParent().getDir() : '';
|
|
91
|
+
if (parent_dir && this.config.name) {
|
|
92
|
+
this._dir = path.resolve(parent_dir, this.config.name);
|
|
93
|
+
}
|
|
99
94
|
}
|
|
100
|
-
this.
|
|
101
|
-
return config;
|
|
95
|
+
return this._dir || '';
|
|
102
96
|
};
|
|
103
97
|
|
|
98
|
+
// ==================== 配置管理 ====================
|
|
99
|
+
|
|
104
100
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
101
|
+
* 预设处理
|
|
102
|
+
* 计算脚本文件路径
|
|
103
|
+
* @private
|
|
107
104
|
*/
|
|
108
|
-
Drive.prototype.
|
|
109
|
-
this.
|
|
105
|
+
Drive.prototype._preset = function () {
|
|
106
|
+
var main = this.config.main;
|
|
107
|
+
if (main && this.config_file) {
|
|
108
|
+
this._script = path.resolve(path.dirname(this.config_file), main);
|
|
109
|
+
}
|
|
110
110
|
};
|
|
111
111
|
|
|
112
112
|
/**
|
|
113
113
|
* 设置配置
|
|
114
|
-
*
|
|
115
|
-
* @
|
|
114
|
+
* 合并配置并触发变更通知
|
|
115
|
+
* @param {object} cfg 配置对象
|
|
116
|
+
* @returns {object} 合并后的配置
|
|
116
117
|
*/
|
|
117
|
-
Drive.prototype.setConfig = function (
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this.is_loaded = false;
|
|
122
|
-
}
|
|
118
|
+
Drive.prototype.setConfig = function (cfg) {
|
|
119
|
+
if (!cfg) {
|
|
120
|
+
return this.config;
|
|
121
|
+
}
|
|
123
122
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
{
|
|
128
|
-
...this.config,
|
|
129
|
-
...config
|
|
130
|
-
},
|
|
131
|
-
this.config_file
|
|
132
|
-
);
|
|
133
|
-
} else {
|
|
134
|
-
Object.assign(this.config, config);
|
|
135
|
-
}
|
|
123
|
+
// 如果 main 改变,重置加载状态
|
|
124
|
+
if (cfg.main !== undefined && cfg.main !== this.config.main) {
|
|
125
|
+
this.is_loaded = false;
|
|
136
126
|
}
|
|
127
|
+
|
|
128
|
+
this.config = { ...this.config, ...cfg };
|
|
137
129
|
this._preset();
|
|
138
|
-
// 通知父级刷新快照
|
|
139
|
-
this._notifySnapshot();
|
|
140
130
|
return this.config;
|
|
141
131
|
};
|
|
142
132
|
|
|
143
133
|
/**
|
|
144
|
-
*
|
|
134
|
+
* 加载配置
|
|
135
|
+
* 从 JSON 配置文件读取配置并合并
|
|
136
|
+
* @param {string} file 配置文件路径
|
|
137
|
+
* @returns {object} 配置对象
|
|
145
138
|
*/
|
|
146
|
-
Drive.prototype.
|
|
147
|
-
if (
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
var parent = this.getParent();
|
|
151
|
-
if (!parent || !parent.config || !parent.config.use_snapshot) {
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
// 防抖:避免频繁写文件,500ms内只刷新一次
|
|
155
|
-
if (this._snapshot_timer) {
|
|
156
|
-
return;
|
|
139
|
+
Drive.prototype.loadConfig = function (file) {
|
|
140
|
+
if (file) {
|
|
141
|
+
this.config_file = file;
|
|
157
142
|
}
|
|
158
|
-
this.
|
|
159
|
-
this._snapshot_timer = null;
|
|
160
|
-
if (parent.refreshSnapshot) {
|
|
161
|
-
parent.refreshSnapshot();
|
|
162
|
-
}
|
|
163
|
-
}, 500);
|
|
143
|
+
return this._loadConfig();
|
|
164
144
|
};
|
|
165
145
|
|
|
166
146
|
/**
|
|
167
|
-
*
|
|
168
|
-
*
|
|
147
|
+
* 内部加载配置
|
|
148
|
+
* 支持热更新模式($.loadJson)和普通模式(require)
|
|
149
|
+
* @returns {object} 配置对象
|
|
150
|
+
* @private
|
|
169
151
|
*/
|
|
170
|
-
Drive.prototype.
|
|
171
|
-
|
|
172
|
-
|
|
152
|
+
Drive.prototype._loadConfig = function () {
|
|
153
|
+
if (!this.config_file) {
|
|
154
|
+
return this.config;
|
|
155
|
+
}
|
|
173
156
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
this._dir = dir;
|
|
184
|
-
} else if (this.config_file) {
|
|
185
|
-
dir = this.config_file.dirname();
|
|
186
|
-
this._dir = dir;
|
|
187
|
-
} else if (this.getParent) {
|
|
188
|
-
let parent_dir = this.getParent()?.getDir() || '';
|
|
189
|
-
if (parent_dir) {
|
|
190
|
-
dir = this.config.name.fullname(parent_dir);
|
|
157
|
+
try {
|
|
158
|
+
var config;
|
|
159
|
+
if (this.mode === 2 || this.mode === 3 || this.mode === 4) {
|
|
160
|
+
// 热更新模式:使用 $.loadJson
|
|
161
|
+
if (typeof $.loadJson === 'function') {
|
|
162
|
+
config = $.loadJson(this.config_file, (cg) => {
|
|
163
|
+
this.setConfig(cg);
|
|
164
|
+
return cg;
|
|
165
|
+
});
|
|
191
166
|
}
|
|
192
167
|
}
|
|
168
|
+
if (!config) {
|
|
169
|
+
// 普通模式:使用 require 或 readFileSync
|
|
170
|
+
try {
|
|
171
|
+
config = require(this.config_file);
|
|
172
|
+
} catch {
|
|
173
|
+
var fs = require('fs');
|
|
174
|
+
var content = fs.readFileSync(this.config_file, 'utf8');
|
|
175
|
+
config = JSON.parse(content);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (config) {
|
|
179
|
+
this.setConfig(config);
|
|
180
|
+
}
|
|
181
|
+
} catch {
|
|
182
|
+
// 配置文件不存在或解析失败,使用默认配置
|
|
193
183
|
}
|
|
194
|
-
return dir;
|
|
195
|
-
};
|
|
196
184
|
|
|
197
|
-
|
|
198
|
-
* 获取模板目录
|
|
199
|
-
* @returns {string} 模板目录
|
|
200
|
-
*/
|
|
201
|
-
Drive.prototype._getTplDir = function () {
|
|
202
|
-
return this.getParent()?.getTplDir() || '';
|
|
185
|
+
return this.config;
|
|
203
186
|
};
|
|
204
187
|
|
|
205
188
|
/**
|
|
206
|
-
*
|
|
189
|
+
* 加载配置文件
|
|
190
|
+
* @param {string} file 文件路径
|
|
191
|
+
* @param {string} name 配置项名称
|
|
192
|
+
* @returns {object|null} 配置对象
|
|
207
193
|
*/
|
|
208
|
-
Drive.prototype.
|
|
209
|
-
let file = this._getScriptFile();
|
|
210
|
-
if (!file) return;
|
|
211
|
-
|
|
194
|
+
Drive.prototype.loadFile = function (file, name) {
|
|
212
195
|
try {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
196
|
+
var fs = require('fs');
|
|
197
|
+
var filename = path.resolve(this.getDir(), file);
|
|
198
|
+
var text = '';
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
text = fs.readFileSync(filename, 'utf8');
|
|
202
|
+
} catch {
|
|
203
|
+
// 文件不存在,创建新的配置文件
|
|
204
|
+
this.newConfig(filename);
|
|
205
|
+
try {
|
|
206
|
+
text = fs.readFileSync(filename, 'utf8');
|
|
207
|
+
} catch {
|
|
208
|
+
return null;
|
|
222
209
|
}
|
|
223
210
|
}
|
|
211
|
+
|
|
212
|
+
if (!text) return null;
|
|
213
|
+
|
|
214
|
+
var config = this._loadHotReload(filename);
|
|
215
|
+
var final_config = this._findConfigByName(config, name);
|
|
216
|
+
return final_config;
|
|
224
217
|
} catch (err) {
|
|
225
|
-
this.log('error',
|
|
218
|
+
this.log('error', '加载配置文件失败: ', err);
|
|
219
|
+
return null;
|
|
226
220
|
}
|
|
227
221
|
};
|
|
228
222
|
|
|
229
223
|
/**
|
|
230
|
-
*
|
|
224
|
+
* 根据模式加载配置(支持热更新)
|
|
225
|
+
* @param {string} file 完整文件路径
|
|
226
|
+
* @returns {object|Array|null} 配置对象
|
|
227
|
+
* @private
|
|
231
228
|
*/
|
|
232
|
-
Drive.prototype.
|
|
233
|
-
this.
|
|
234
|
-
|
|
235
|
-
|
|
229
|
+
Drive.prototype._loadHotReload = function (file) {
|
|
230
|
+
if (this.mode === 2 || this.mode === 3 || this.mode === 4) {
|
|
231
|
+
if (typeof $.loadJson === 'function') {
|
|
232
|
+
return $.loadJson(file, this._handleHotReload.bind(this));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
return require(file);
|
|
237
|
+
} catch {
|
|
238
|
+
var fs = require('fs');
|
|
239
|
+
var content = fs.readFileSync(file, 'utf8');
|
|
240
|
+
return JSON.parse(content);
|
|
241
|
+
}
|
|
236
242
|
};
|
|
237
243
|
|
|
238
244
|
/**
|
|
239
|
-
*
|
|
240
|
-
* @
|
|
245
|
+
* 热更新回调处理
|
|
246
|
+
* @param {object} config 配置对象
|
|
247
|
+
* @private
|
|
241
248
|
*/
|
|
242
|
-
Drive.prototype.
|
|
243
|
-
this.
|
|
244
|
-
|
|
249
|
+
Drive.prototype._handleHotReload = function (config) {
|
|
250
|
+
this.setConfig(config);
|
|
251
|
+
this._reloadIfNeeded();
|
|
252
|
+
return config;
|
|
245
253
|
};
|
|
246
254
|
|
|
247
255
|
/**
|
|
248
|
-
*
|
|
249
|
-
* @param {
|
|
256
|
+
* 根据名称在配置数组中查找配置项
|
|
257
|
+
* @param {object|Array} config 配置对象或数组
|
|
258
|
+
* @param {string} name 配置项名称
|
|
259
|
+
* @returns {object|null} 匹配的配置项
|
|
260
|
+
* @private
|
|
250
261
|
*/
|
|
251
|
-
Drive.prototype.
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
262
|
+
Drive.prototype._findConfigByName = function (config, name) {
|
|
263
|
+
if (!name || !Array.isArray(config)) return config;
|
|
264
|
+
return config.find((item) => item.name === name) || null;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* 根据模式决定是否重新加载脚本
|
|
269
|
+
* @private
|
|
270
|
+
*/
|
|
271
|
+
Drive.prototype._reloadIfNeeded = function () {
|
|
272
|
+
if (this.mode === 3 || this.mode === 4) {
|
|
273
|
+
this.reloadScript();
|
|
256
274
|
}
|
|
257
275
|
};
|
|
258
276
|
|
|
277
|
+
// ==================== 脚本管理 ====================
|
|
278
|
+
|
|
259
279
|
/**
|
|
260
280
|
* 获取脚本文件路径
|
|
261
|
-
* @returns {string
|
|
281
|
+
* @returns {string|null} 完整文件路径
|
|
282
|
+
* @private
|
|
262
283
|
*/
|
|
263
284
|
Drive.prototype._getScriptFile = function () {
|
|
264
|
-
|
|
285
|
+
var main = this.config.main || '';
|
|
265
286
|
if (!main) return null;
|
|
266
287
|
|
|
267
|
-
|
|
268
|
-
|
|
288
|
+
var filename = path.resolve(this.getDir(), main);
|
|
289
|
+
var fs = require('fs');
|
|
290
|
+
if (!fs.existsSync(filename)) {
|
|
269
291
|
this.newScript(filename);
|
|
270
292
|
}
|
|
271
293
|
return filename;
|
|
272
294
|
};
|
|
273
295
|
|
|
274
296
|
/**
|
|
275
|
-
*
|
|
276
|
-
*
|
|
297
|
+
* 加载脚本
|
|
298
|
+
* 检查 main 配置,require 脚本文件并注册方法
|
|
299
|
+
* @returns {boolean} 是否加载成功
|
|
300
|
+
* @private
|
|
277
301
|
*/
|
|
278
|
-
Drive.prototype.
|
|
279
|
-
this.
|
|
280
|
-
|
|
302
|
+
Drive.prototype._loadScript = function () {
|
|
303
|
+
var main = this.config.main;
|
|
304
|
+
if (!main) {
|
|
305
|
+
// 配置型模块,无脚本
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
281
308
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
* @returns {boolean} 创建是否成功
|
|
286
|
-
*/
|
|
287
|
-
Drive.prototype._createScriptFile = function (file) {
|
|
288
|
-
var tpl = this._getScriptTpl();
|
|
289
|
-
var content = $.tpl.render(tpl, this.getModel('script'));
|
|
290
|
-
if (!content) {
|
|
291
|
-
return '';
|
|
309
|
+
var script_path = this._script;
|
|
310
|
+
if (!script_path) {
|
|
311
|
+
return false;
|
|
292
312
|
}
|
|
293
|
-
let code = this.prettyCode(content, 'js');
|
|
294
|
-
file.saveText(code);
|
|
295
|
-
return file;
|
|
296
|
-
};
|
|
297
313
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
*/
|
|
302
|
-
Drive.prototype._getScriptTpl = function () {
|
|
303
|
-
var f = './script.tpl.js'.fullname(this._getTplDir());
|
|
304
|
-
if (f.hasFile()) {
|
|
305
|
-
return f.loadText();
|
|
306
|
-
} else {
|
|
307
|
-
return `module.exports = {
|
|
308
|
-
/**
|
|
309
|
-
* 初始化
|
|
310
|
-
*/
|
|
311
|
-
async _init() {
|
|
312
|
-
this.log('debug', \`初始化!\`);
|
|
313
|
-
},
|
|
314
|
-
/**
|
|
315
|
-
* 启动
|
|
316
|
-
*/
|
|
317
|
-
async _start() {
|
|
318
|
-
this.log('debug', \`启动!\`);
|
|
319
|
-
},
|
|
320
|
-
/**
|
|
321
|
-
* 停止
|
|
322
|
-
*/
|
|
323
|
-
async _stop() {
|
|
324
|
-
this.log('debug', \`停止!\`);
|
|
325
|
-
},
|
|
326
|
-
/**
|
|
327
|
-
* 销毁
|
|
328
|
-
*/
|
|
329
|
-
async _destroy(...args) {
|
|
330
|
-
this.log('debug', \`销毁!\`);
|
|
331
|
-
},
|
|
332
|
-
/**
|
|
333
|
-
* 主要逻辑
|
|
334
|
-
*/
|
|
335
|
-
async main(...args) {
|
|
336
|
-
// 主要代码写在这
|
|
314
|
+
var fs = require('fs');
|
|
315
|
+
if (!fs.existsSync(script_path)) {
|
|
316
|
+
return false;
|
|
337
317
|
}
|
|
338
|
-
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
var exports;
|
|
321
|
+
if (this.mode === 3 || this.mode === 4) {
|
|
322
|
+
// 开发模式:使用 $.require 进行热重载
|
|
323
|
+
if (typeof $.require === 'function') {
|
|
324
|
+
exports = $.require(script_path, (mod) => {
|
|
325
|
+
this._setMainMethod(mod);
|
|
326
|
+
});
|
|
327
|
+
} else {
|
|
328
|
+
exports = require(script_path);
|
|
329
|
+
}
|
|
330
|
+
} else {
|
|
331
|
+
// 生产模式:清除缓存后 require
|
|
332
|
+
try {
|
|
333
|
+
var resolved = require.resolve(script_path);
|
|
334
|
+
if (require.cache[resolved]) {
|
|
335
|
+
delete require.cache[resolved];
|
|
336
|
+
}
|
|
337
|
+
} catch {
|
|
338
|
+
// 模块未缓存,忽略
|
|
339
|
+
}
|
|
340
|
+
exports = require(script_path);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
this._setMainMethod(exports);
|
|
344
|
+
this.is_loaded = true;
|
|
345
|
+
return true;
|
|
346
|
+
} catch {
|
|
347
|
+
// 脚本加载失败
|
|
348
|
+
this.is_loaded = false;
|
|
349
|
+
return false;
|
|
339
350
|
}
|
|
340
351
|
};
|
|
341
352
|
|
|
342
353
|
/**
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
* @returns {object | null} 加载的模块对象
|
|
354
|
+
* 加载脚本文件(外部接口)
|
|
355
|
+
* 在开发模式下使用 $.require 进行热重载,在生产模式下使用标准 require
|
|
356
|
+
* @returns {object|null} 加载的模块对象
|
|
347
357
|
*/
|
|
348
358
|
Drive.prototype.loadScript = function () {
|
|
349
|
-
|
|
359
|
+
var file = this._getScriptFile();
|
|
350
360
|
if (!file) return null;
|
|
361
|
+
|
|
351
362
|
if (this.mode === 3 || this.mode === 4) {
|
|
352
|
-
// 开发模式:使用 $.require
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
this._setMainMethod(cs);
|
|
361
|
-
return cs;
|
|
363
|
+
// 开发模式:使用 $.require 进行热重载
|
|
364
|
+
if (typeof $.require === 'function') {
|
|
365
|
+
var cs = $.require(file, (mod) => {
|
|
366
|
+
this._setMainMethod(mod);
|
|
367
|
+
});
|
|
368
|
+
this._setMainMethod(cs);
|
|
369
|
+
return cs;
|
|
370
|
+
}
|
|
362
371
|
}
|
|
372
|
+
|
|
373
|
+
var cs = require(file);
|
|
374
|
+
this._setMainMethod(cs);
|
|
375
|
+
return cs;
|
|
363
376
|
};
|
|
364
377
|
|
|
365
378
|
/**
|
|
366
379
|
* 设置主方法
|
|
367
|
-
*
|
|
380
|
+
* 从脚本导出对象中注册方法和数据
|
|
381
|
+
* @param {object} mod 脚本导出对象
|
|
382
|
+
* @private
|
|
368
383
|
*/
|
|
369
384
|
Drive.prototype._setMainMethod = function (mod) {
|
|
370
385
|
if (!mod) return;
|
|
386
|
+
|
|
371
387
|
var name = this.config.func_name || 'main';
|
|
372
388
|
if (name && mod[name]) {
|
|
373
|
-
this.
|
|
389
|
+
this._main_func = mod[name];
|
|
374
390
|
}
|
|
391
|
+
|
|
375
392
|
var new_keys = Object.keys(mod);
|
|
376
393
|
// 热重载时如果脚本未变化则跳过重建
|
|
377
394
|
if (this._script_keys && this._script_keys.length === new_keys.length) {
|
|
@@ -387,355 +404,516 @@ Drive.prototype._setMainMethod = function (mod) {
|
|
|
387
404
|
return;
|
|
388
405
|
}
|
|
389
406
|
}
|
|
407
|
+
|
|
390
408
|
this._script_keys = new_keys;
|
|
391
409
|
for (var key in mod) {
|
|
392
410
|
var obj = mod[key];
|
|
393
411
|
if (typeof obj === 'function') {
|
|
394
412
|
this.setMethod(key, obj);
|
|
395
413
|
} else {
|
|
396
|
-
|
|
414
|
+
try {
|
|
415
|
+
// eslint-disable-next-line mm_eslint/function-name
|
|
416
|
+
this.setData(key, obj);
|
|
417
|
+
} catch {
|
|
418
|
+
// 忽略类型错误
|
|
419
|
+
}
|
|
397
420
|
}
|
|
398
421
|
}
|
|
399
422
|
this.is_loaded = true;
|
|
400
423
|
};
|
|
401
424
|
|
|
402
425
|
/**
|
|
403
|
-
*
|
|
404
|
-
* @
|
|
405
|
-
* @returns {string} 配置文件作用域
|
|
426
|
+
* 移除加载的脚本模块
|
|
427
|
+
* @private
|
|
406
428
|
*/
|
|
407
|
-
Drive.prototype.
|
|
408
|
-
|
|
409
|
-
|
|
429
|
+
Drive.prototype._remove = function () {
|
|
430
|
+
var file = this._getScriptFile();
|
|
431
|
+
if (!file) return;
|
|
410
432
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
433
|
+
try {
|
|
434
|
+
if (this.mode === 3 || this.mode === 4) {
|
|
435
|
+
// 移除模块和监听
|
|
436
|
+
if (typeof $.unload === 'function') {
|
|
437
|
+
$.unload(file);
|
|
438
|
+
}
|
|
439
|
+
} else {
|
|
440
|
+
// 移除模块缓存
|
|
441
|
+
try {
|
|
442
|
+
var filename = require.resolve(file);
|
|
443
|
+
if (require.cache[filename]) {
|
|
444
|
+
delete require.cache[filename];
|
|
445
|
+
}
|
|
446
|
+
} catch {
|
|
447
|
+
// 忽略
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
} catch (err) {
|
|
451
|
+
this.log('error', '移除加载的脚本模块失败: ', err);
|
|
419
452
|
}
|
|
420
|
-
this._createConfigFile(file);
|
|
421
453
|
};
|
|
422
454
|
|
|
423
455
|
/**
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
* @param {string} type 预试类型,可选"json"或"js"
|
|
427
|
-
* @returns {string} 预试后的代码字符串
|
|
456
|
+
* 卸载脚本
|
|
457
|
+
* 清除 require.cache,重置加载状态
|
|
428
458
|
*/
|
|
429
|
-
Drive.prototype.
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
switch (type) {
|
|
434
|
-
case 'json5':
|
|
435
|
-
options.parser = 'json5';
|
|
436
|
-
break;
|
|
437
|
-
case 'js':
|
|
438
|
-
case 'javascript':
|
|
439
|
-
options.parser = 'babel';
|
|
440
|
-
options.semi = true; // 在语句末尾添加分号
|
|
441
|
-
options.singleQuote = true; // 使用单引号
|
|
442
|
-
break;
|
|
443
|
-
default:
|
|
444
|
-
options.parser = 'json';
|
|
445
|
-
break;
|
|
446
|
-
}
|
|
447
|
-
let new_code = $.prettyCode(code, options);
|
|
448
|
-
return new_code;
|
|
459
|
+
Drive.prototype.unloadScript = function () {
|
|
460
|
+
this._remove();
|
|
461
|
+
this._script_keys = null;
|
|
462
|
+
this.is_loaded = false;
|
|
449
463
|
};
|
|
450
464
|
|
|
451
465
|
/**
|
|
452
|
-
*
|
|
453
|
-
* @
|
|
454
|
-
* @returns {object} 模块模型
|
|
466
|
+
* 重新加载脚本
|
|
467
|
+
* @returns {object|null} 返回加载的模块对象
|
|
455
468
|
*/
|
|
456
|
-
Drive.prototype.
|
|
457
|
-
|
|
469
|
+
Drive.prototype.reloadScript = function () {
|
|
470
|
+
this.unloadScript();
|
|
471
|
+
return this.loadScript();
|
|
458
472
|
};
|
|
459
473
|
|
|
460
474
|
/**
|
|
461
|
-
*
|
|
462
|
-
* @param {string} file 目标文件路径
|
|
463
|
-
* @param {object} model 模块模型
|
|
464
|
-
* @returns {string} 配置文件内容
|
|
475
|
+
* 加载前处理
|
|
465
476
|
*/
|
|
466
|
-
Drive.prototype.
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
477
|
+
Drive.prototype.loadBefore = function () {
|
|
478
|
+
try {
|
|
479
|
+
var module = this.loadScript();
|
|
480
|
+
if (module) {
|
|
481
|
+
this.is_loaded = true;
|
|
482
|
+
}
|
|
483
|
+
} catch (err) {
|
|
484
|
+
this.log('error', '加载前处理失败: ', err);
|
|
485
|
+
this.is_loaded = false;
|
|
486
|
+
}
|
|
472
487
|
};
|
|
473
488
|
|
|
474
|
-
|
|
475
|
-
* 创建配置文件
|
|
476
|
-
* @param {string} file 目标文件路径
|
|
477
|
-
* @param {object} model 模块模型
|
|
478
|
-
* @returns {string} 配置文件内容
|
|
479
|
-
*/
|
|
480
|
-
Drive.prototype.createConfigFile = function (file, model) {
|
|
481
|
-
file.addDir();
|
|
482
|
-
return this._createConfigFile(file, model);
|
|
483
|
-
};
|
|
489
|
+
// ==================== 文件操作 ====================
|
|
484
490
|
|
|
485
491
|
/**
|
|
486
|
-
*
|
|
487
|
-
* @returns {string} 配置模板内容
|
|
492
|
+
* 保存配置
|
|
488
493
|
*/
|
|
489
|
-
Drive.prototype.
|
|
490
|
-
var
|
|
491
|
-
if (
|
|
492
|
-
return f.loadText();
|
|
493
|
-
} else {
|
|
494
|
-
return `{
|
|
495
|
-
// 模块名称,必填
|
|
496
|
-
"name": "\${name}",
|
|
497
|
-
// 模块标题,可选,默认"示例标题"
|
|
498
|
-
"title": "\${title || '示例标题'}",
|
|
499
|
-
// 模块描述,可选,默认"示例描述"
|
|
500
|
-
"description": "\${description || '示例描述'}",
|
|
501
|
-
// 主脚本文件路径,可选,默认"./index.js"
|
|
502
|
-
"main": "\${main || './index.js'}",
|
|
503
|
-
// 模块作用域,可选,默认"server"
|
|
504
|
-
"scope": "\${scope || 'server'}",
|
|
505
|
-
// 模块状态,可选,默认1
|
|
506
|
-
"state": \${state || 1},
|
|
507
|
-
// 模块排序,可选,默认100
|
|
508
|
-
"sort": \${sort || 100},
|
|
509
|
-
// 是否结束,可选,默认false
|
|
510
|
-
"end": \${end || false}
|
|
511
|
-
}`;
|
|
512
|
-
}
|
|
513
|
-
};
|
|
494
|
+
Drive.prototype.save = function () {
|
|
495
|
+
var file = this._script || this.config_file;
|
|
496
|
+
if (!file) return;
|
|
514
497
|
|
|
515
|
-
|
|
516
|
-
* 加载配置文件
|
|
517
|
-
* @param {string} file 文件路径
|
|
518
|
-
* @param {string} name 配置项名称
|
|
519
|
-
* @returns {object | null} 配置对象
|
|
520
|
-
*/
|
|
521
|
-
Drive.prototype.loadFile = function (file, name) {
|
|
498
|
+
var fs = require('fs');
|
|
522
499
|
try {
|
|
523
|
-
|
|
524
|
-
let text = filename.loadText();
|
|
500
|
+
var text = fs.readFileSync(file, 'utf8');
|
|
525
501
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
502
|
+
if (text && text.trim().startsWith('[')) {
|
|
503
|
+
// 数组格式配置
|
|
504
|
+
var config = JSON.parse(text);
|
|
505
|
+
if (Array.isArray(config)) {
|
|
506
|
+
var index = config.findIndex((item) => item.name === this.config.name);
|
|
507
|
+
if (index !== -1) {
|
|
508
|
+
config[index] = this.config;
|
|
509
|
+
} else {
|
|
510
|
+
config.push(this.config);
|
|
511
|
+
}
|
|
512
|
+
fs.writeFileSync(file, JSON.stringify(config, null, 2), 'utf8');
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
530
515
|
}
|
|
531
516
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
let config = this._loadHotReload(filename);
|
|
535
|
-
let final_config = this._findConfigByName(config, name);
|
|
536
|
-
return final_config;
|
|
517
|
+
// 单对象格式配置,直接保存
|
|
518
|
+
fs.writeFileSync(file, JSON.stringify(this.config, null, 2), 'utf8');
|
|
537
519
|
} catch (err) {
|
|
538
|
-
this.log('error',
|
|
539
|
-
return null;
|
|
540
|
-
}
|
|
541
|
-
};
|
|
542
|
-
|
|
543
|
-
/**
|
|
544
|
-
* 根据模式加载配置(支持热更新)
|
|
545
|
-
* @param {string} file 完整文件路径
|
|
546
|
-
* @returns {object | Array | null} 配置对象
|
|
547
|
-
*/
|
|
548
|
-
Drive.prototype._loadHotReload = function (file) {
|
|
549
|
-
if (this.mode === 2 || this.mode === 3 || this.mode === 4) {
|
|
550
|
-
return $.loadJson(file, this._handleHotReload.bind(this));
|
|
520
|
+
this.log('error', '保存配置失败: ', err);
|
|
551
521
|
}
|
|
552
|
-
return file.loadJson();
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
/**
|
|
556
|
-
* 根据名称在配置数组中查找配置项
|
|
557
|
-
* @param {object | Array} config 配置对象或数组
|
|
558
|
-
* @param {string} name 配置项名称
|
|
559
|
-
* @returns {object | null} 匹配的配置项
|
|
560
|
-
*/
|
|
561
|
-
Drive.prototype._findConfigByName = function (config, name) {
|
|
562
|
-
if (!name || !Array.isArray(config)) return config;
|
|
563
|
-
return config.find((Drive) => Drive.name === name) || null;
|
|
564
522
|
};
|
|
565
523
|
|
|
566
524
|
/**
|
|
567
|
-
*
|
|
525
|
+
* 删除项目
|
|
526
|
+
* @returns {string|null} 删除的目录路径
|
|
568
527
|
*/
|
|
569
|
-
Drive.prototype.
|
|
570
|
-
|
|
571
|
-
|
|
528
|
+
Drive.prototype.remove = function () {
|
|
529
|
+
var dir = this.getDir();
|
|
530
|
+
if (dir) {
|
|
531
|
+
var fs = require('fs');
|
|
532
|
+
try {
|
|
533
|
+
fs.rmdirSync(dir, { recursive: true });
|
|
534
|
+
} catch {
|
|
535
|
+
// 忽略
|
|
536
|
+
}
|
|
572
537
|
}
|
|
538
|
+
return dir;
|
|
573
539
|
};
|
|
574
540
|
|
|
575
541
|
/**
|
|
576
542
|
* 删除目录
|
|
577
543
|
*/
|
|
578
544
|
Drive.prototype.delDir = function () {
|
|
579
|
-
|
|
580
|
-
if (
|
|
581
|
-
|
|
545
|
+
var dir = this.getDir();
|
|
546
|
+
if (dir) {
|
|
547
|
+
var fs = require('fs');
|
|
548
|
+
try {
|
|
549
|
+
fs.rmdirSync(dir, { recursive: true });
|
|
550
|
+
} catch {
|
|
551
|
+
// 忽略
|
|
552
|
+
}
|
|
582
553
|
}
|
|
583
554
|
};
|
|
584
555
|
|
|
585
556
|
/**
|
|
586
557
|
* 删除配置和脚本文件
|
|
587
|
-
* @returns {string
|
|
558
|
+
* @returns {string|null} 错误消息,如果没有错误则返回 null
|
|
588
559
|
*/
|
|
589
560
|
Drive.prototype.delFile = function () {
|
|
590
|
-
|
|
591
|
-
|
|
561
|
+
var fs = require('fs');
|
|
562
|
+
var name = this.config.name;
|
|
563
|
+
var file = this._script || this.config_file;
|
|
592
564
|
|
|
593
|
-
let error_message = null;
|
|
594
565
|
try {
|
|
595
|
-
if (!file
|
|
596
|
-
|
|
597
|
-
if (!file.hasFile()) {
|
|
598
|
-
return '配置文件不存在';
|
|
599
|
-
}
|
|
566
|
+
if (!file) return null;
|
|
567
|
+
if (!fs.existsSync(file)) return '文件不存在';
|
|
600
568
|
|
|
601
|
-
|
|
569
|
+
return this._delConfigFile(file, name);
|
|
602
570
|
} catch (err) {
|
|
603
|
-
this.log('error',
|
|
604
|
-
|
|
571
|
+
this.log('error', '删除文件失败: ', err);
|
|
572
|
+
return '删除失败: ' + (err.message || '');
|
|
605
573
|
}
|
|
606
|
-
return error_message;
|
|
607
574
|
};
|
|
608
575
|
|
|
609
576
|
/**
|
|
610
577
|
* 删除配置文件
|
|
611
|
-
* @param {
|
|
578
|
+
* @param {string} file 文件路径
|
|
612
579
|
* @param {string} name 配置名称
|
|
613
|
-
* @returns {string
|
|
580
|
+
* @returns {string|null} 错误消息
|
|
581
|
+
* @private
|
|
614
582
|
*/
|
|
615
583
|
Drive.prototype._delConfigFile = function (file, name) {
|
|
616
|
-
|
|
617
|
-
|
|
584
|
+
var fs = require('fs');
|
|
585
|
+
try {
|
|
586
|
+
var text = fs.readFileSync(file, 'utf8');
|
|
587
|
+
if (!text) {
|
|
588
|
+
this.delDir();
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
var config = JSON.parse(text);
|
|
593
|
+
if (Array.isArray(config)) {
|
|
594
|
+
return this._delArray(file, config, name);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
this.delDir();
|
|
598
|
+
return null;
|
|
599
|
+
} catch {
|
|
618
600
|
this.delDir();
|
|
619
601
|
return null;
|
|
620
602
|
}
|
|
621
|
-
|
|
622
|
-
let config = text.toJson();
|
|
623
|
-
if (Array.isArray(config)) {
|
|
624
|
-
return this._delArray(file, config, name);
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
this.delDir();
|
|
628
|
-
return null;
|
|
629
603
|
};
|
|
630
604
|
|
|
631
605
|
/**
|
|
632
606
|
* 从数组配置中删除指定项
|
|
633
|
-
* @param {
|
|
607
|
+
* @param {string} file 文件路径
|
|
634
608
|
* @param {Array} config 配置数组
|
|
635
609
|
* @param {string} name 配置名称
|
|
636
|
-
* @returns {string
|
|
610
|
+
* @returns {string|null} 错误消息
|
|
611
|
+
* @private
|
|
637
612
|
*/
|
|
638
613
|
Drive.prototype._delArray = function (file, config, name) {
|
|
639
|
-
|
|
614
|
+
var fs = require('fs');
|
|
615
|
+
var index = config.findIndex((item) => item.name === name);
|
|
640
616
|
if (index === -1) return null;
|
|
641
617
|
|
|
642
|
-
this.delDir();
|
|
643
618
|
config.splice(index, 1);
|
|
644
619
|
|
|
645
620
|
if (config.length > 0) {
|
|
646
|
-
|
|
647
|
-
} else
|
|
648
|
-
|
|
621
|
+
fs.writeFileSync(file, JSON.stringify(config, null, 2), 'utf8');
|
|
622
|
+
} else {
|
|
623
|
+
try {
|
|
624
|
+
fs.unlinkSync(file);
|
|
625
|
+
} catch {
|
|
626
|
+
// 忽略
|
|
627
|
+
}
|
|
649
628
|
}
|
|
650
629
|
|
|
630
|
+
this.delDir();
|
|
651
631
|
return null;
|
|
652
632
|
};
|
|
653
633
|
|
|
654
634
|
/**
|
|
655
635
|
* 获取配置文件名称
|
|
656
|
-
* @param {
|
|
636
|
+
* @param {string} file 文件路径
|
|
657
637
|
* @returns {string} 配置文件名称
|
|
638
|
+
* @private
|
|
658
639
|
*/
|
|
659
640
|
Drive.prototype._getName = function (file) {
|
|
660
|
-
|
|
661
|
-
return name;
|
|
662
|
-
// // 提取文件名作为模块名
|
|
663
|
-
// var word = new Word();
|
|
664
|
-
// // 将文件名改为驼峰命名法
|
|
665
|
-
// return word.toCamelCase(name);
|
|
641
|
+
return path.basename(path.dirname(file));
|
|
666
642
|
};
|
|
667
643
|
|
|
668
644
|
/**
|
|
669
|
-
*
|
|
645
|
+
* 获取配置文件作用域
|
|
646
|
+
* @param {string} file 文件路径
|
|
647
|
+
* @returns {string} 配置文件作用域
|
|
648
|
+
* @private
|
|
670
649
|
*/
|
|
671
|
-
Drive.prototype.
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
650
|
+
Drive.prototype._getScope = function (file) {
|
|
651
|
+
var normalized = file.replace(/\\/g, '/');
|
|
652
|
+
var idx = normalized.indexOf('app/');
|
|
653
|
+
if (idx === -1) return '';
|
|
654
|
+
return normalized.substring(idx + 4, normalized.indexOf('/', idx + 4));
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
// ==================== 模板/文件创建 ====================
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* 获取脚本模板
|
|
661
|
+
* @returns {string} 脚本模板内容
|
|
662
|
+
* @private
|
|
663
|
+
*/
|
|
664
|
+
Drive.prototype._getScriptTpl = function () {
|
|
665
|
+
var fs = require('fs');
|
|
666
|
+
var f = path.resolve(this.getTplDir(), 'script.tpl.js');
|
|
667
|
+
if (fs.existsSync(f)) {
|
|
668
|
+
return fs.readFileSync(f, 'utf8');
|
|
669
|
+
}
|
|
670
|
+
return 'module.exports = {\n' +
|
|
671
|
+
' /**\n' +
|
|
672
|
+
' * 初始化\n' +
|
|
673
|
+
' */\n' +
|
|
674
|
+
' async _init() {\n' +
|
|
675
|
+
" this.log('debug', '初始化!');\n" +
|
|
676
|
+
' },\n' +
|
|
677
|
+
' /**\n' +
|
|
678
|
+
' * 启动\n' +
|
|
679
|
+
' */\n' +
|
|
680
|
+
' async _start() {\n' +
|
|
681
|
+
" this.log('debug', '启动!');\n" +
|
|
682
|
+
' },\n' +
|
|
683
|
+
' /**\n' +
|
|
684
|
+
' * 停止\n' +
|
|
685
|
+
' */\n' +
|
|
686
|
+
' async _stop() {\n' +
|
|
687
|
+
" this.log('debug', '停止!');\n" +
|
|
688
|
+
' },\n' +
|
|
689
|
+
' /**\n' +
|
|
690
|
+
' * 销毁\n' +
|
|
691
|
+
' */\n' +
|
|
692
|
+
' async _destroy() {\n' +
|
|
693
|
+
" this.log('debug', '销毁!');\n" +
|
|
694
|
+
' },\n' +
|
|
695
|
+
' /**\n' +
|
|
696
|
+
' * 主要逻辑\n' +
|
|
697
|
+
' */\n' +
|
|
698
|
+
' async main() {\n' +
|
|
699
|
+
' // 主要代码写在这\n' +
|
|
700
|
+
' }\n' +
|
|
701
|
+
'};\n';
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* 获取配置模板
|
|
706
|
+
* @returns {string} 配置模板内容
|
|
707
|
+
* @private
|
|
708
|
+
*/
|
|
709
|
+
Drive.prototype._getConfigTpl = function () {
|
|
710
|
+
var fs = require('fs');
|
|
711
|
+
var f = path.resolve(this.getTplDir(), 'config.tpl.json');
|
|
712
|
+
if (fs.existsSync(f)) {
|
|
713
|
+
return fs.readFileSync(f, 'utf8');
|
|
680
714
|
}
|
|
715
|
+
return '{\n' +
|
|
716
|
+
' "name": "${name}",\n' +
|
|
717
|
+
' "title": "${title || \'示例标题\'}",\n' +
|
|
718
|
+
' "description": "${description || \'示例描述\'}",\n' +
|
|
719
|
+
' "main": "${main || \'./index.js\'}",\n' +
|
|
720
|
+
' "scope": "${scope || \'server\'}",\n' +
|
|
721
|
+
' "state": ${state || 1},\n' +
|
|
722
|
+
' "sort": ${sort || 100}\n' +
|
|
723
|
+
'}\n';
|
|
681
724
|
};
|
|
682
725
|
|
|
683
726
|
/**
|
|
684
|
-
*
|
|
727
|
+
* 获取模块模型
|
|
728
|
+
* @param {string} type 模型类型,可选 "config" 或 "script"
|
|
729
|
+
* @returns {object} 模块模型
|
|
685
730
|
*/
|
|
686
|
-
Drive.prototype.
|
|
687
|
-
|
|
688
|
-
// this.loadScript();
|
|
731
|
+
Drive.prototype.getModel = function (type) {
|
|
732
|
+
return this.config;
|
|
689
733
|
};
|
|
690
734
|
|
|
691
735
|
/**
|
|
692
|
-
*
|
|
736
|
+
* 新建配置文件
|
|
737
|
+
* @param {string} file 目标文件路径
|
|
693
738
|
*/
|
|
694
|
-
Drive.prototype.
|
|
695
|
-
|
|
696
|
-
|
|
739
|
+
Drive.prototype.newConfig = function (file) {
|
|
740
|
+
if (!this.config.name) {
|
|
741
|
+
this.config.name = this._getName(file);
|
|
742
|
+
this.config.scope = this._getScope(file);
|
|
743
|
+
}
|
|
744
|
+
this._createConfigFile(file);
|
|
745
|
+
};
|
|
697
746
|
|
|
698
|
-
|
|
699
|
-
|
|
747
|
+
/**
|
|
748
|
+
* 创建配置文件
|
|
749
|
+
* @param {string} file 目标文件路径
|
|
750
|
+
* @param {object} model 模块模型
|
|
751
|
+
* @returns {string} 文件路径
|
|
752
|
+
*/
|
|
753
|
+
Drive.prototype._createConfigFile = function (file, model) {
|
|
754
|
+
var fs = require('fs');
|
|
755
|
+
var tpl = this._getConfigTpl();
|
|
756
|
+
var content = this._renderTpl(tpl, model || this.getModel('config'));
|
|
757
|
+
var code = this.prettyCode(content, 'json');
|
|
758
|
+
var dir = path.dirname(file);
|
|
759
|
+
if (!fs.existsSync(dir)) {
|
|
760
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
761
|
+
}
|
|
762
|
+
fs.writeFileSync(file, code, 'utf8');
|
|
763
|
+
return file;
|
|
764
|
+
};
|
|
700
765
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
766
|
+
/**
|
|
767
|
+
* 创建配置文件(带目录创建)
|
|
768
|
+
* @param {string} file 目标文件路径
|
|
769
|
+
* @param {object} model 模块模型
|
|
770
|
+
* @returns {string} 文件路径
|
|
771
|
+
*/
|
|
772
|
+
Drive.prototype.createConfigFile = function (file, model) {
|
|
773
|
+
var fs = require('fs');
|
|
774
|
+
var dir = path.dirname(file);
|
|
775
|
+
if (!fs.existsSync(dir)) {
|
|
776
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
777
|
+
}
|
|
778
|
+
return this._createConfigFile(file, model);
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* 新建脚本文件
|
|
783
|
+
* @param {string} file 目标文件路径
|
|
784
|
+
*/
|
|
785
|
+
Drive.prototype.newScript = function (file) {
|
|
786
|
+
this._createScriptFile(file);
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* 创建脚本文件
|
|
791
|
+
* @param {string} file 文件路径
|
|
792
|
+
* @returns {string} 文件路径
|
|
793
|
+
* @private
|
|
794
|
+
*/
|
|
795
|
+
Drive.prototype._createScriptFile = function (file) {
|
|
796
|
+
var fs = require('fs');
|
|
797
|
+
var tpl = this._getScriptTpl();
|
|
798
|
+
var content = this._renderTpl(tpl, this.getModel('script'));
|
|
799
|
+
var code = this.prettyCode(content, 'js');
|
|
800
|
+
var dir = path.dirname(file);
|
|
801
|
+
if (!fs.existsSync(dir)) {
|
|
802
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
803
|
+
}
|
|
804
|
+
fs.writeFileSync(file, code, 'utf8');
|
|
805
|
+
return file;
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* 渲染模板(简单替换 ${key} 占位符)
|
|
810
|
+
* @param {string} tpl 模板内容
|
|
811
|
+
* @param {object} model 模型数据
|
|
812
|
+
* @returns {string} 渲染后的内容
|
|
813
|
+
* @private
|
|
814
|
+
*/
|
|
815
|
+
Drive.prototype._renderTpl = function (tpl, model) {
|
|
816
|
+
return tpl.replace(/\$\{([^}]+)\}/g, (match, key) => {
|
|
817
|
+
// 处理默认值表达式:${key || 'default'}
|
|
818
|
+
var parts = key.split('||').map((s) => s.trim());
|
|
819
|
+
var var_name = parts[0];
|
|
820
|
+
var default_val = '';
|
|
821
|
+
if (parts.length > 1) {
|
|
822
|
+
default_val = parts[1].replace(/^['"]|['"]$/g, '');
|
|
716
823
|
}
|
|
824
|
+
var val = model ? model[var_name] : undefined;
|
|
825
|
+
return val !== undefined && val !== null ? val : default_val;
|
|
826
|
+
});
|
|
827
|
+
};
|
|
717
828
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
829
|
+
/**
|
|
830
|
+
* 美化代码
|
|
831
|
+
* @param {string} code 代码字符串
|
|
832
|
+
* @param {string} type 代码类型,可选 "json" 或 "js"
|
|
833
|
+
* @returns {string} 美化后的代码
|
|
834
|
+
*/
|
|
835
|
+
Drive.prototype.prettyCode = function (code, type) {
|
|
836
|
+
if (typeof $.prettyCode === 'function') {
|
|
837
|
+
var options = {
|
|
838
|
+
tab_width: 2
|
|
839
|
+
};
|
|
840
|
+
switch (type) {
|
|
841
|
+
case 'json5':
|
|
842
|
+
case 'json':
|
|
843
|
+
options.parser = 'json5';
|
|
844
|
+
break;
|
|
845
|
+
case 'js':
|
|
846
|
+
case 'javascript':
|
|
847
|
+
options.parser = 'babel';
|
|
848
|
+
options.semi = true;
|
|
849
|
+
options.singleQuote = true;
|
|
850
|
+
break;
|
|
851
|
+
default:
|
|
852
|
+
options.parser = 'json';
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
855
|
+
return $.prettyCode(code, options);
|
|
722
856
|
}
|
|
857
|
+
return code;
|
|
723
858
|
};
|
|
724
859
|
|
|
860
|
+
// ==================== 生命周期重写 ====================
|
|
861
|
+
|
|
725
862
|
/**
|
|
726
|
-
*
|
|
863
|
+
* 加载核心
|
|
864
|
+
* 加载配置文件和脚本文件
|
|
865
|
+
* @returns {Promise<void>}
|
|
866
|
+
* @private
|
|
867
|
+
* @override
|
|
727
868
|
*/
|
|
728
|
-
Drive.prototype.
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
869
|
+
Drive.prototype._loadCore = async function () {
|
|
870
|
+
if (this.lifecycle_state !== 'created') {
|
|
871
|
+
throw new Error('模块未创建,无法加载');
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
this.emit('load:before');
|
|
875
|
+
|
|
876
|
+
try {
|
|
877
|
+
// 加载配置
|
|
878
|
+
this._loadConfig();
|
|
879
|
+
// 加载脚本
|
|
880
|
+
this._loadScript();
|
|
881
|
+
this.lifecycle_state = 'loaded';
|
|
882
|
+
this.emit('load:success');
|
|
883
|
+
} catch (err) {
|
|
884
|
+
this.emit('load:error', err);
|
|
885
|
+
throw err;
|
|
732
886
|
}
|
|
733
|
-
return dir;
|
|
734
887
|
};
|
|
735
888
|
|
|
736
889
|
/**
|
|
737
|
-
*
|
|
890
|
+
* 结束核心
|
|
891
|
+
* 先调用 _end 钩子,再卸载脚本
|
|
892
|
+
* @returns {Promise<void>}
|
|
893
|
+
* @private
|
|
894
|
+
* @override
|
|
738
895
|
*/
|
|
739
|
-
|
|
740
|
-
|
|
896
|
+
Drive.prototype._endCore = async function () {
|
|
897
|
+
if (this.lifecycle_state !== 'started' && this.lifecycle_state !== 'paused') {
|
|
898
|
+
throw new Error('模块未启动或暂停,无法结束');
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
this.emit('end:before');
|
|
902
|
+
|
|
903
|
+
try {
|
|
904
|
+
var end_hook = this.getMethod('_end');
|
|
905
|
+
if (end_hook) {
|
|
906
|
+
end_hook(this);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// 卸载脚本
|
|
910
|
+
this.unloadScript();
|
|
911
|
+
this.lifecycle_state = 'ended';
|
|
912
|
+
this.emit('end:success');
|
|
913
|
+
} catch (err) {
|
|
914
|
+
this.emit('end:error', err);
|
|
915
|
+
throw err;
|
|
916
|
+
}
|
|
741
917
|
};
|
|
918
|
+
|
|
919
|
+
module.exports = { Drive };
|