jj.js 0.19.0 → 0.20.0
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/README.md +243 -173
- package/jj.js +14 -14
- package/lib/app.js +52 -17
- package/lib/cache.js +96 -30
- package/lib/config.js +14 -4
- package/lib/context.js +18 -22
- package/lib/controller.js +36 -5
- package/lib/cookie.js +11 -15
- package/lib/ctx.js +28 -28
- package/lib/db/mongodb.js +564 -0
- package/lib/db/mysql.js +32 -28
- package/lib/db/sql.js +13 -3
- package/lib/db/sqlite.js +32 -24
- package/lib/db.js +230 -112
- package/lib/loader.js +79 -44
- package/lib/logger.js +124 -126
- package/lib/middleware.js +39 -6
- package/lib/model.js +23 -9
- package/lib/{run.js → mvc.js} +29 -22
- package/lib/pagination.js +68 -14
- package/lib/request.js +4 -4
- package/lib/response.js +70 -6
- package/lib/router.js +4 -3
- package/lib/types.js +363 -196
- package/lib/upload.js +15 -15
- package/lib/url.js +10 -2
- package/lib/utils/error.js +9 -5
- package/lib/utils/utils.js +2 -0
- package/lib/utils/validate.js +13 -0
- package/lib/view.js +54 -8
- package/logo.png +0 -0
- package/package.json +8 -4
- package/types.js +177 -83
- package/CHANGELOG.md +0 -58
- package/jsdoc.conf.js +0 -17
package/lib/types.js
CHANGED
|
@@ -1,242 +1,409 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
2
3
|
const fsPromises = require('fs').promises;
|
|
3
4
|
const {app: cfg_app} = require('./config');
|
|
4
5
|
const toHump = require('./utils/str').toHump;
|
|
5
6
|
const Logger = require('./logger');
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
|
-
*
|
|
9
|
+
* @typedef {Object} NodeInfo
|
|
10
|
+
* @property {string} node_name - 节点名称
|
|
11
|
+
* @property {string} [file_name] - 文件名
|
|
12
|
+
* @property {string} file_type - 文件类型 (file/dir)
|
|
13
|
+
* @property {boolean} [is_class] - 是否为类
|
|
14
|
+
* @property {Object<string, NodeInfo>} [children] - 子节点
|
|
9
15
|
*/
|
|
10
|
-
function start() {
|
|
11
|
-
const watch = require('watch');
|
|
12
|
-
const filter = function(f, stat) {
|
|
13
|
-
const static_dir = cfg_app.static_dir ? path.join(cfg_app.base_dir, cfg_app.static_dir) : '';
|
|
14
|
-
return (!~f.indexOf('.') || /\.js(on)?$/.test(f)) && !~f.indexOf('types.js') && (static_dir && !~f.indexOf(static_dir));
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
let prevFile = {file: null,action: null,stat: null};
|
|
18
|
-
watch.watchTree(cfg_app.base_dir, {ignoreDotFiles: true, ignoreDirectoryPattern: /node_modules/, filter}, (f, curr, prev) => {
|
|
19
|
-
if(typeof f == "object" && prev === null && curr === null) {
|
|
20
|
-
// Finished walking the tree
|
|
21
|
-
Object.keys(f).forEach(file => {
|
|
22
|
-
Logger.system("wacth:", file);
|
|
23
|
-
});
|
|
24
|
-
createFile();
|
|
25
|
-
} else if(prev === null) {
|
|
26
|
-
// f is a new file
|
|
27
|
-
if(prevFile.file != f || prevFile.action != "created") {
|
|
28
|
-
prevFile = {file: f, action: "created", stat: curr};
|
|
29
|
-
createFile(f);
|
|
30
|
-
}
|
|
31
|
-
} else if(curr.nlink === 0) {
|
|
32
|
-
// f was removed
|
|
33
|
-
if(prevFile.file != f || prevFile.action != "removed") {
|
|
34
|
-
prevFile = { file: f, action: "removed", stat: curr };
|
|
35
|
-
createFile(f);
|
|
36
|
-
}
|
|
37
|
-
} else {
|
|
38
|
-
// f was changed
|
|
39
|
-
if(prevFile.file === null) {
|
|
40
|
-
createFile(f);
|
|
41
|
-
} else {
|
|
42
|
-
// stat might return null, so catch errors
|
|
43
|
-
try {
|
|
44
|
-
if (prevFile.stat.mtime.getTime() !== curr.mtime.getTime()) {
|
|
45
|
-
createFile(f);
|
|
46
|
-
}
|
|
47
|
-
} catch(e) {
|
|
48
|
-
createFile(f);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
16
|
|
|
55
17
|
/**
|
|
56
|
-
*
|
|
18
|
+
* @typedef {Object} TypesGeneratorState
|
|
19
|
+
* @property {string|null} _f - 当前处理的文件路径
|
|
20
|
+
* @property {string} _APP - 当前应用名称
|
|
21
|
+
* @property {fs.FSWatcher|null} _watcher - 文件监听器
|
|
57
22
|
*/
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {Object} PrevFile
|
|
26
|
+
* @property {string|null} file
|
|
27
|
+
* @property {string|null} action
|
|
28
|
+
* @property {import('fs').Stats|null} stat
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* TypesGenerator 类型定义生成器
|
|
33
|
+
*/
|
|
34
|
+
class TypesGenerator {
|
|
35
|
+
constructor() {
|
|
36
|
+
/** @type {TypesGeneratorState} */
|
|
37
|
+
this.state = {
|
|
38
|
+
_f: null,
|
|
39
|
+
_APP: cfg_app.default_app,
|
|
40
|
+
_watcher: null
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 启动文件监听并自动生成类型定义
|
|
46
|
+
* @returns {fs.FSWatcher|null}
|
|
47
|
+
*/
|
|
48
|
+
watch() {
|
|
49
|
+
const filter = (/** @type {string} */ f) => {
|
|
50
|
+
const static_dir = cfg_app.static_dir ? path.join(cfg_app.base_dir, cfg_app.static_dir) : '';
|
|
51
|
+
if((/\.js(on)?$/.test(f)) && ~f.indexOf('types.js')) return false;
|
|
52
|
+
if(static_dir && ~f.indexOf(static_dir)) return false;
|
|
53
|
+
return true;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** @type {PrevFile} */
|
|
57
|
+
let prevFile = {file: null, action: null, stat: null};
|
|
58
|
+
/** @type {NodeJS.Timeout|null} */
|
|
59
|
+
let debounceTimer = null;
|
|
60
|
+
const DEBOUNCE_DELAY = 300;
|
|
61
|
+
|
|
62
|
+
this.state._watcher = fs.watch(cfg_app.base_dir, { recursive: true }, (eventType, filename) => {
|
|
63
|
+
if (debounceTimer) {
|
|
64
|
+
clearTimeout(debounceTimer);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
debounceTimer = setTimeout(() => {
|
|
68
|
+
this.handleFileChange(eventType, filename, filter, prevFile);
|
|
69
|
+
}, DEBOUNCE_DELAY);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// 初始扫描
|
|
73
|
+
this.scanDirectory(cfg_app.base_dir, (/** @type {string} */ file) => {
|
|
74
|
+
Logger.system("watch:", file);
|
|
75
|
+
}).then(() => {
|
|
76
|
+
this.createFile();
|
|
77
|
+
});
|
|
64
78
|
|
|
65
|
-
|
|
66
|
-
this._f = f;
|
|
67
|
-
|
|
68
|
-
const f_info = f.replace(cfg_app.base_dir + path.sep, '').split(path.sep);
|
|
69
|
-
if(cfg_app.app_multi && f_info[0] && f_info[1] && f_info[0] != cfg_app.common_app && f_info[0] != 'config' && !~f_info[0].indexOf('.')) {
|
|
70
|
-
this._APP = f_info[0];
|
|
71
|
-
}
|
|
72
|
-
} else {
|
|
73
|
-
this._APP = cfg_app.default_app;
|
|
79
|
+
return this.state._watcher;
|
|
74
80
|
}
|
|
75
|
-
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
/**
|
|
83
|
+
* 处理文件变化回调
|
|
84
|
+
* @param {string} eventType - 事件类型 (rename/change)
|
|
85
|
+
* @param {string|null} filename - 文件名
|
|
86
|
+
* @param {function(string): boolean} filter - 过滤器函数
|
|
87
|
+
* @param {PrevFile} prevFile - 上一个文件信息
|
|
88
|
+
*/
|
|
89
|
+
handleFileChange(eventType, filename, filter, prevFile) {
|
|
90
|
+
if (!filename) return;
|
|
91
|
+
|
|
92
|
+
const fullPath = path.join(cfg_app.base_dir, filename);
|
|
93
|
+
|
|
94
|
+
if (!filter(filename)) return;
|
|
95
|
+
if (filename.includes('node_modules')) return;
|
|
80
96
|
|
|
81
|
-
|
|
82
|
-
|
|
97
|
+
try {
|
|
98
|
+
const stat = fs.statSync(fullPath);
|
|
99
|
+
|
|
100
|
+
if (eventType === 'rename') {
|
|
101
|
+
if (fs.existsSync(fullPath)) {
|
|
102
|
+
if (prevFile.file != fullPath || prevFile.action != "created") {
|
|
103
|
+
prevFile.file = fullPath;
|
|
104
|
+
prevFile.action = "created";
|
|
105
|
+
prevFile.stat = stat;
|
|
106
|
+
this.createFile(fullPath);
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
if (prevFile.file != fullPath || prevFile.action != "removed") {
|
|
110
|
+
prevFile.file = fullPath;
|
|
111
|
+
prevFile.action = "removed";
|
|
112
|
+
prevFile.stat = stat;
|
|
113
|
+
this.createFile(fullPath);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
} else if (eventType === 'change') {
|
|
117
|
+
if (prevFile.file === null || prevFile.file != fullPath) {
|
|
118
|
+
prevFile.file = fullPath;
|
|
119
|
+
prevFile.action = "change";
|
|
120
|
+
prevFile.stat = stat;
|
|
121
|
+
this.createFile(fullPath);
|
|
122
|
+
} else {
|
|
123
|
+
try {
|
|
124
|
+
if (!prevFile.stat || prevFile.stat.mtime.getTime() !== stat.mtime.getTime()) {
|
|
125
|
+
prevFile.file = fullPath;
|
|
126
|
+
prevFile.action = "change";
|
|
127
|
+
prevFile.stat = stat;
|
|
128
|
+
this.createFile(fullPath);
|
|
129
|
+
}
|
|
130
|
+
} catch(e) {
|
|
131
|
+
prevFile.file = fullPath;
|
|
132
|
+
prevFile.action = "change";
|
|
133
|
+
prevFile.stat = stat;
|
|
134
|
+
this.createFile(fullPath);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} catch (err) {
|
|
139
|
+
// 忽略统计错误
|
|
140
|
+
}
|
|
141
|
+
}
|
|
83
142
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
143
|
+
/**
|
|
144
|
+
* 递归扫描目录
|
|
145
|
+
* @param {string} dir
|
|
146
|
+
* @param {function(string): void} callback
|
|
147
|
+
* @param {string[]} ignoreDirs
|
|
148
|
+
* @returns {Promise<void>}
|
|
149
|
+
*/
|
|
150
|
+
async scanDirectory(dir, callback, ignoreDirs = ['node_modules']) {
|
|
151
|
+
try {
|
|
152
|
+
const files = await fsPromises.readdir(dir, { withFileTypes: true });
|
|
153
|
+
|
|
154
|
+
for (const file of files) {
|
|
155
|
+
const fullPath = path.join(dir, file.name);
|
|
156
|
+
|
|
157
|
+
if (file.isDirectory()) {
|
|
158
|
+
if (!ignoreDirs.includes(file.name)) {
|
|
159
|
+
callback(fullPath);
|
|
160
|
+
await this.scanDirectory(fullPath, callback, ignoreDirs);
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
callback(fullPath);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
} catch (err) {
|
|
167
|
+
Logger.system('scanDirectory error:', err);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
92
170
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
types += `/**\n * @typedef {typeof import('${path}')} ${type_name}\n */\n\n`;
|
|
104
|
-
} else {
|
|
105
|
-
types += `/**\n * @typedef {typeof import('${path}')} ${type_name}Class\n`;
|
|
106
|
-
types += ` * @typedef {typeof import('${path}').prototype} ${type_name}Instance\n`;
|
|
107
|
-
types += ` * @typedef {(${type_name}Class & ${type_name}Instance)} ${type_name}\n */\n\n`;
|
|
171
|
+
/**
|
|
172
|
+
* 创建类型定义文件
|
|
173
|
+
* @param {string} [f] - 触发变更的文件路径
|
|
174
|
+
* @returns {Promise<void>}
|
|
175
|
+
*/
|
|
176
|
+
async createFile(f) {
|
|
177
|
+
if(f) {
|
|
178
|
+
require.cache[f] && delete(require.cache[f]);
|
|
179
|
+
if(f == this.state._f) {
|
|
180
|
+
return;
|
|
108
181
|
}
|
|
182
|
+
|
|
183
|
+
this.state._f && require.cache[this.state._f] && delete(require.cache[this.state._f]);
|
|
184
|
+
this.state._f = f;
|
|
185
|
+
|
|
186
|
+
const f_info = f.replace(cfg_app.base_dir + path.sep, '').split(path.sep);
|
|
187
|
+
if(cfg_app.app_multi && f_info[0] && f_info[1] && f_info[0] != cfg_app.common_app && f_info[0] != 'config' && !~f_info[0].indexOf('.')) {
|
|
188
|
+
this.state._APP = f_info[0];
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
this.state._APP = cfg_app.default_app;
|
|
109
192
|
}
|
|
193
|
+
|
|
110
194
|
|
|
111
|
-
|
|
112
|
-
|
|
195
|
+
const ignore = ['node_modules', 'docker', 'package-lock.json', 'types.js', '.git', '.gitignore'];
|
|
196
|
+
ignore.push(path.basename(require.main?.filename || 'server.js'));
|
|
197
|
+
cfg_app.static_dir && ignore.push(path.join(cfg_app.base_dir, cfg_app.static_dir));
|
|
113
198
|
|
|
114
|
-
|
|
115
|
-
|
|
199
|
+
const node_list = await this.getNodeList('.', ignore);
|
|
200
|
+
this.calcNodeList(node_list, this.state._APP);
|
|
201
|
+
|
|
202
|
+
const types_tpl = require('./tpl/types');
|
|
203
|
+
const types_str = types_tpl.replace('__TYPES__', this.createTypes(node_list)).replace('__PROPERTY__', this.createProps(node_list));
|
|
204
|
+
const types_js = path.join(cfg_app.base_dir, 'types.js');
|
|
205
|
+
fsPromises.writeFile(types_js, types_str).then(_ => {
|
|
206
|
+
Logger.system('createTypes success:', types_js);
|
|
207
|
+
}).catch(err => {
|
|
208
|
+
Logger.system('createTypes error:', err);
|
|
209
|
+
});
|
|
116
210
|
}
|
|
117
211
|
|
|
118
|
-
|
|
119
|
-
|
|
212
|
+
/**
|
|
213
|
+
* 生成类型定义
|
|
214
|
+
* @param {Object<string, NodeInfo>} node_list
|
|
215
|
+
* @param {string} [base_type]
|
|
216
|
+
* @returns {string}
|
|
217
|
+
*/
|
|
218
|
+
createTypes(node_list, base_type = '') {
|
|
219
|
+
let types = '';
|
|
220
|
+
let props = '';
|
|
221
|
+
Object.entries(node_list).forEach(([path, node]) => {
|
|
222
|
+
const type_name = toHump(base_type + '_' + node.node_name);
|
|
223
|
+
if(node.file_type == 'dir' && node.children) {
|
|
224
|
+
types += this.createTypes(node.children, type_name);
|
|
225
|
+
} else {
|
|
226
|
+
if(!node.is_class) {
|
|
227
|
+
types += `/**\n * @typedef {typeof import('${path}')} ${type_name}\n */\n\n`;
|
|
228
|
+
} else {
|
|
229
|
+
types += `/**\n * @typedef {typeof import('${path}')} ${type_name}Class\n`;
|
|
230
|
+
types += ` * @typedef {typeof import('${path}').prototype} ${type_name}Instance\n`;
|
|
231
|
+
types += ` * @typedef {(${type_name}Class & ${type_name}Instance)} ${type_name}\n */\n\n`;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
120
234
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
let props = '';
|
|
235
|
+
props += ` * @property {${type_name}} ${node.node_name} - ${path}\n`;
|
|
236
|
+
});
|
|
124
237
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if(type_name != 'Config') {
|
|
128
|
-
props += `\n /** @type {${type_name}} */\n $${node.node_name}\n`;
|
|
238
|
+
if(base_type) {
|
|
239
|
+
types += `/**\n * @typedef {object} ${base_type}\n${props} */\n\n`;
|
|
129
240
|
}
|
|
130
|
-
});
|
|
131
241
|
|
|
132
|
-
|
|
133
|
-
if(node_list['./config']) {
|
|
134
|
-
type_config = '(JJConfig & Config)';
|
|
242
|
+
return types;
|
|
135
243
|
}
|
|
136
|
-
props += `\n /** @type {${type_config}} */\n $config\n`;
|
|
137
244
|
|
|
138
|
-
|
|
139
|
-
|
|
245
|
+
/**
|
|
246
|
+
* 生成智能属性
|
|
247
|
+
* @param {Object<string, NodeInfo>} node_list
|
|
248
|
+
* @returns {string}
|
|
249
|
+
*/
|
|
250
|
+
createProps(node_list) {
|
|
251
|
+
let props = '';
|
|
140
252
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const file_name = dirent.name;
|
|
148
|
-
const file_path = dir + '/' + file_name;
|
|
149
|
-
const node_name = path.parse(file_name).name;
|
|
150
|
-
const abs_path = path.join(cfg_app.base_dir, file_path);
|
|
151
|
-
if(ignore.filter(n => abs_path.includes(n)).length) {
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
253
|
+
Object.entries(node_list).forEach(([path, node]) => {
|
|
254
|
+
const type_name = toHump('_' + node.node_name);
|
|
255
|
+
if(type_name != 'Config') {
|
|
256
|
+
props += `\n /** @type {${type_name}} */\n $${node.node_name}\n`;
|
|
257
|
+
}
|
|
258
|
+
});
|
|
154
259
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if(file_type == 'file' && !regFile) {
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
if(file_type == 'dir' && !regDir) {
|
|
162
|
-
continue;
|
|
260
|
+
let type_config = 'JJConfig';
|
|
261
|
+
if(node_list['./config']) {
|
|
262
|
+
type_config = '(JJConfig & Config)';
|
|
163
263
|
}
|
|
264
|
+
props += `\n /** @type {${type_config}} */\n $config\n`;
|
|
164
265
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
266
|
+
return props;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* 获取节点对象
|
|
271
|
+
* @param {string} dir
|
|
272
|
+
* @param {string[]} ignore
|
|
273
|
+
* @returns {Promise<Object<string, NodeInfo>>}
|
|
274
|
+
*/
|
|
275
|
+
async getNodeList(dir, ignore = ['node_modules', 'docker', 'package-lock.json', '.git', '.gitignore']) {
|
|
276
|
+
const isClass = require('is-class');
|
|
277
|
+
const files = await fsPromises.readdir(path.join(cfg_app.base_dir, dir), {withFileTypes: true});
|
|
278
|
+
/** @type {Object<string, NodeInfo>} */
|
|
279
|
+
const type_list = {};
|
|
280
|
+
for(const dirent of files) {
|
|
281
|
+
const file_name = dirent.name;
|
|
282
|
+
const file_path = dir + '/' + file_name;
|
|
283
|
+
const node_name = path.parse(file_name).name;
|
|
284
|
+
const abs_path = path.join(cfg_app.base_dir, file_path);
|
|
285
|
+
if(ignore.filter(n => abs_path.includes(n)).length) {
|
|
286
|
+
continue;
|
|
171
287
|
}
|
|
172
|
-
} else if(file_type == 'dir') {
|
|
173
|
-
type_list[file_path] = {node_name, file_name, file_type, children: await getNodeList(file_path, ignore)};;
|
|
174
|
-
}
|
|
175
288
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
289
|
+
const file_type = dirent.isFile() ? 'file' : dirent.isDirectory() ? 'dir' : '';
|
|
290
|
+
const regFile = /.+\.js(on)?$/.test(file_name);
|
|
291
|
+
const regDir = !file_name.includes('.');
|
|
292
|
+
if(file_type == 'file' && !regFile) {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
if(file_type == 'dir' && !regDir) {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
179
298
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
299
|
+
if(file_type == 'file') {
|
|
300
|
+
try {
|
|
301
|
+
// @ts-ignore
|
|
302
|
+
type_list[file_path] = {node_name, file_name, file_type, is_class: isClass(require(abs_path))};
|
|
303
|
+
} catch(e) {
|
|
304
|
+
// 包含语法错误时,require会出错
|
|
305
|
+
}
|
|
306
|
+
} else if(file_type == 'dir') {
|
|
307
|
+
type_list[file_path] = {node_name, file_name, file_type, children: await this.getNodeList(file_path, ignore)};
|
|
308
|
+
}
|
|
187
309
|
}
|
|
188
|
-
|
|
189
|
-
return copy_node_list;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// 处理common库
|
|
193
|
-
function calcNodeList(node_list, _APP) {
|
|
194
|
-
// 处理common
|
|
195
|
-
const node_common = './' + cfg_app.common_app;
|
|
196
|
-
if(cfg_app.common_app && node_list[node_common] && node_list[node_common].children) {
|
|
197
|
-
const children = copyNodeList(node_list[node_common].children);
|
|
198
|
-
Object.entries(children).forEach(([path, node]) => {
|
|
199
|
-
node_list[path] = node;
|
|
200
|
-
});
|
|
310
|
+
return type_list;
|
|
201
311
|
}
|
|
202
312
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const p_commmon = p.replace(node_app, node_common);
|
|
216
|
-
node_list[path_common].children[p_commmon] && delete node_list[path_common].children[p_commmon];
|
|
217
|
-
node_list[path_common].children[p] = n;
|
|
218
|
-
});
|
|
313
|
+
/**
|
|
314
|
+
* 复制子节点
|
|
315
|
+
* @param {Object<string, NodeInfo>} node_list
|
|
316
|
+
* @returns {Object<string, NodeInfo>}
|
|
317
|
+
*/
|
|
318
|
+
copyNodeList(node_list) {
|
|
319
|
+
/** @type {Object<string, NodeInfo>} */
|
|
320
|
+
const copy_node_list = {};
|
|
321
|
+
Object.entries(node_list).forEach(([path, node]) => {
|
|
322
|
+
copy_node_list[path] = {...node};
|
|
323
|
+
if(node.children) {
|
|
324
|
+
copy_node_list[path].children = this.copyNodeList(node.children);
|
|
219
325
|
}
|
|
220
326
|
});
|
|
327
|
+
return copy_node_list;
|
|
221
328
|
}
|
|
222
329
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
330
|
+
/**
|
|
331
|
+
* 处理common库
|
|
332
|
+
* @param {Object<string, NodeInfo>} node_list
|
|
333
|
+
* @param {string} _APP
|
|
334
|
+
*/
|
|
335
|
+
calcNodeList(node_list, _APP) {
|
|
336
|
+
// 处理common
|
|
337
|
+
const node_common = './' + cfg_app.common_app;
|
|
338
|
+
if(cfg_app.common_app && node_list[node_common] && node_list[node_common].children) {
|
|
339
|
+
const children = this.copyNodeList(node_list[node_common].children);
|
|
340
|
+
Object.entries(children).forEach(([path, node]) => {
|
|
341
|
+
node_list[path] = node;
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// 处理app
|
|
346
|
+
const node_app = './' + _APP;
|
|
347
|
+
if(_APP && node_list[node_app] && node_list[node_app].children) {
|
|
348
|
+
Object.entries(node_list[node_app].children).forEach(([path, node]) => {
|
|
349
|
+
const path_common = path.replace(node_app, node_common);
|
|
350
|
+
if(!node_list[path_common]) {
|
|
351
|
+
node_list[path] = node;
|
|
352
|
+
} else if(node_list[path_common].file_type == 'file') {
|
|
353
|
+
delete node_list[path_common];
|
|
354
|
+
node_list[path] = node;
|
|
355
|
+
} else if(node.children) {
|
|
356
|
+
Object.entries(node.children).forEach(([p, n]) => {
|
|
357
|
+
const p_commmon = p.replace(node_app, node_common);
|
|
358
|
+
if (node_list[path_common].children) {
|
|
359
|
+
node_list[path_common].children[p_commmon] && delete node_list[path_common].children[p_commmon];
|
|
360
|
+
node_list[path_common].children[p] = n;
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// 特殊处理view和response
|
|
368
|
+
const node_view = './' + _APP + '/view';
|
|
369
|
+
const common_view = './' + cfg_app.common_app + '/view';
|
|
370
|
+
if(node_list[node_view] && !node_list[node_view].is_class) {
|
|
371
|
+
delete node_list[node_view];
|
|
372
|
+
}
|
|
373
|
+
if(node_list[common_view] && !node_list[common_view].is_class) {
|
|
374
|
+
delete node_list[common_view];
|
|
375
|
+
}
|
|
376
|
+
const node_response = './' + _APP + '/response';
|
|
377
|
+
const common_response = './' + cfg_app.common_app + '/response';
|
|
378
|
+
if(node_list[node_response] && !node_list[node_response].is_class) {
|
|
379
|
+
delete node_list[node_response];
|
|
380
|
+
}
|
|
381
|
+
if(node_list[common_response] && !node_list[common_response].is_class) {
|
|
382
|
+
delete node_list[common_response];
|
|
383
|
+
}
|
|
239
384
|
}
|
|
240
385
|
}
|
|
241
386
|
|
|
242
|
-
|
|
387
|
+
// 创建单例实例
|
|
388
|
+
const generator = new TypesGenerator();
|
|
389
|
+
|
|
390
|
+
module.exports = {
|
|
391
|
+
/**
|
|
392
|
+
* 启动文件监听并自动生成类型定义
|
|
393
|
+
* @returns {fs.FSWatcher|null}
|
|
394
|
+
*/
|
|
395
|
+
watch: () => generator.watch(),
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* 手动创建类型定义文件
|
|
399
|
+
* @param {string} [f] - 触发变更的文件路径
|
|
400
|
+
* @returns {Promise<void>}
|
|
401
|
+
*/
|
|
402
|
+
createFile: (f) => generator.createFile(f),
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* 获取生成器实例
|
|
406
|
+
* @returns {TypesGenerator}
|
|
407
|
+
*/
|
|
408
|
+
generator
|
|
409
|
+
};
|