forked from NEIAPI/nei-toolkit
-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.js
executable file
·323 lines (310 loc) · 10.2 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
/*
* exports toolkit
* @author genify(caijf@corp.netease.com)
* @author huntbao
*/
'use strict';
let fs = require('fs');
let util = require('util');
let path = require('path');
let _fs = require('./lib/util/file');
let _path = require('./lib/util/path');
let _util = require('./lib/util/util');
let _io = require('./lib/util/io');
let _log = require('./lib/util/logger');
let Builder = require('./lib/nei/builder');
let server = require('./lib/server/server');
let neiDbConst = require('./lib/fb-modules/config/db.json');
let _logger = _log.logger;
let Template = require('./lib/nei/template');
class Main {
/**
* 构建 nei 工程
* @param {object} arg - 参数类的实例
* @param {string} action - 操作命令
* @param {object} args - 命令行参数对象
*/
build(arg, action, args) {
this.args = args;
this.config = {
action: action
};
let cwd = process.cwd() + '/';
this.checkConfig();
let loadedHandler = (ds) => {
this.config.pid = ds.project && ds.project.id || ds.specs && ds.specs[0] && ds.specs[0]["spec"]["id"];
this.ds = ds;
this.fillArgs();
// 合并完参数后, 需要重新 format 一下, 并且此时需要取默认值
this.args = arg.format(this.config.action, this.args, true);
// output 的参数有可能写在项目或者规范中, 这里重新计算, 然后还需要重新检查
this.config.outputRoot = _path.normalize(_path.absolute((this.args.output || './') + '/', cwd));
this.config.neiConfigRoot = `${this.config.outputRoot}nei.${this.config.pid}.${this.args.key || this.args.specKey}/`;
this.checkConfig();
new Builder({
config: this.config,
args: this.args,
ds: this.ds
});
}
this.loadData(loadedHandler);
}
/**
*
*/
template(arg, data) {
if (!arg.path) {
_logger.error(`未指定本地模板路径,请通过nei template -h 查看帮助`);
return;
}
new Template(arg, data).build();
}
/**
* 更新 nei 工程规范
* @param {object} arg - 参数类的实例
* @param {string} action - 操作命令
* @param {object} args - 命令行参数对象
*/
update(arg, action, args) {
let dir = path.join(process.cwd(), args.output || './');
let projects = this.findProjects(args);
let buildProject = (neiProjectDir, exitIfNotExist) => {
let config = _util.file2json(`${neiProjectDir}/nei.json`, exitIfNotExist, "未找到nei.json文件,请检查,建议使用nei build重新构建");
let mergedArgs = Object.assign({}, config.args, args);
if(mergedArgs.add){// 只有update支持add,这里可能会有重复,由builder里去重
mergedArgs.ids = mergedArgs.ids.concat(mergedArgs.add);
}
new Main().build(arg, action, mergedArgs);
}
if (args.key) {
if (projects.length == 0) {
_logger.error(`在 ${dir} 中找不到 key 为 ${args.key} 的项目, 请检查`);
return process.exit(1);
} else if (projects.length > 1) {
_logger.error(`存在多个 key 为 ${args.key} 的项目, 请检查`);
return process.exit(1);
} else {
buildProject(projects[0], true);
}
} else {
if (projects.length > 1) {
if (!args.all) {
_logger.error(`存在多个 nei 项目, 请通过 key 参数指定需要更新的项目, 或者使用 --all 参数更新所有项目`);
return process.exit(1);
} else {
projects.forEach(buildProject);
}
} else {
buildProject(projects[0], true);
}
}
}
/**
* start mock server
* @param {object} args - args object
*/
server(args) {
let dir = path.join(process.cwd(), args.output || './');
let tryStartServer = (configFilePath) => {
if (_fs.exist(configFilePath)) {
let options = {
configFilePath: configFilePath,
fromNei: true,
targetDir: dir
};
server(options);
} else {
_logger.warn(`文件不存在: ${configFilePath}`);
}
}
let projects = this.findProjects(args);
if (projects.length === 0) {
if (args.key) {
_logger.error(`在 ${dir} 中找不到 key 为 ${args.key} 的项目, 请检查`);
} else {
_logger.error(`在 ${dir} 中找不到 nei 项目, 请检查`);
}
return process.exit(1);
} else if (projects.length > 1) {
if (args.key) {
_logger.error(`在 ${dir} 中找到多个 key 为 ${args.key} 的项目, 请检查`);
} else if (args.all) {
// 合并启动服务器
let routes = {};
let temp = {};
projects.forEach(it => {
try {
temp = require(`${it}/server.config.js`);
Object.assign(routes, temp.routes);
} catch (e) {
_logger.error(`找不到文件${it}/server.config.js`);
}
});
temp.routes = routes;
let options = {
configFilePath: temp,
fromNei: true,
watchConfigPaths: projects.map(it => `${it}/server.config.js`),
targetDir: dir
};
server(options);
} else {
_logger.error(`在 ${dir} 中找到多个 nei 项目, 请使用 key 参数指定要启动的项目, 或使用--all选项合并路由`);
return process.exit(1);
}
} else {
tryStartServer(`${projects[0]}/server.config.js`);
}
}
/**
* 从 NEI 服务器加载项目数据
* @param {function} callback - 加载成功回调
*/
loadData(callback) {
let neiHost = 'https://nei.netease.com/';
let url;
if (this.args.hasOwnProperty('specKey')) {
let specKey = this.args.specKey;
url = `${neiHost}/api/specificationres/?key=${encodeURIComponent(specKey)}`;
} else {
let key = this.args.key;
let specType = {
web: neiDbConst.CMN_TYP_WEB,
aos: neiDbConst.CMN_TYP_AOS,
ios: neiDbConst.CMN_TYP_IOS,
test: neiDbConst.CMN_TYP_TEST
}[this.args.specType] || neiDbConst.CMN_TYP_WEB;
url = `${neiHost}/api/projectres/?key=${encodeURIComponent(key)}&spectype=${specType}`;
}
url = _path.normalize(url);
_logger.info('从 NEI 服务器加载数据, 地址: %s', url);
_io.download(url, (data) => {
_logger.info(`数据加载完成, 开始解析数据`);
let json;
try {
json = JSON.parse(data);
if (json.code !== 200) {
_logger.error('NEI 服务器异常', json);
process.exit(1);
}
} catch (ex) {
_logger.debug('NEI 数据 \n%s', data);
_logger.error('NEI 数据解析错误: \n%s', ex.stack);
process.exit(1);
}
callback(json.result);
});
}
/**
* 检测指定的目录中是否存在 nei 配置文件
*/
checkConfig() {
let action = this.config.action;
let outputDir = this.config.outputRoot;
if (!_fs.exist(outputDir)) {
// 目录不存在, update 命令不会走到这里
return;
}
let result = {};
_fs.walk(outputDir,
filename => {
if (path.basename(filename) === 'nei.json')
result['file'] = true;
return filename;
},
(dirname, r, level) => {
if (level > 1) {
return false;
}
let basename = path.basename(dirname);
if (basename.startsWith('nei') && basename.endsWith(this.args.key || this.args.specKey)) {
result['dir'] = basename;
}
return dirname;
}, result, 0);
let foundConfigFile = null;
if (result['file'] && result['dir']) {
foundConfigFile = result['dir'];
}
let errorMsg = null;
if (foundConfigFile) {
if (action === 'build') {
errorMsg = '项目 %s 已存在, 请使用 nei update 命令更新项目';
} else if (action === 'update') {
_logger.debug('更新项目 %s', foundConfigFile);
}
} else {
if (action === 'update') {
errorMsg = '没找到通过 nei 构建的项目, 请先使用 nei build 命令构建项目';
}
}
if (errorMsg) {
_logger.error(errorMsg, foundConfigFile);
return process.exit(1);
}
}
/**
* 填充参数, 合并项目中的命令行参数设置、规范中的命令行参数
*/
fillArgs() {
let spec = this.ds.specs[0];
if (!spec) {
_logger.error(`项目没找到工程规范, 请检查是否已经给项目指定了规范, 或者 specType 参数值是否正确`);
return process.exit(1);
}
let specArgsConfig = spec.spec.argsConfig;
let proArgs = {};
this.ds.cliargs && this.ds.cliargs.forEach(function (cliarg) {
proArgs[cliarg.key] = cliarg.value;
});
let specCliArgDoc = null;
let findSpecCliArg = (docs) => {
specCliArgDoc = docs.find((doc) => {
return doc.id === specArgsConfig;
});
if (!specCliArgDoc) {
docs.forEach((doc) => {
findSpecCliArg(doc.children);
});
}
}
let specArgs = {};
// 如果规范设置了命令行参数文件
if (specArgsConfig) {
findSpecCliArg(spec.docs);
if (specCliArgDoc) {
try {
specArgs = JSON.parse(specCliArgDoc.content);
} catch (e) {
_logger.error(`规范设置的命令行参数文件, 它的内容不是有效的 json, 请检查是否有多余的逗号或者缺少逗号, 键及字符串类型的值是否都使用了英文双引号等: ${e}`);
}
}
}
this.args = Object.assign({}, specArgs, proArgs, this.args);
}
/**
* 查找指定输出目录下的 nei 项目
*/
findProjects(args) {
let dir = path.join(process.cwd(), args.output || './');
if (!_fs.exist(dir)) {
// 目录不存在, 退出程序
_logger.error(`项目目录 ${dir} 不存在, 请检查`);
return process.exit(1);
}
let files = fs.readdirSync(dir);
let projects = [];
files.forEach((file) => {
if (args.key) {
if (file.startsWith('nei') && file.endsWith(args.key)) {
projects.push(`${dir}/${file}`);
}
} else if (file.startsWith('nei') && file.length >= 42) {
// 疑是 nei 项目, 42 是 nei 配置文件的长度, 考虑到项目有可能会超过 5 位, 这里使用 >=
projects.push(`${dir}/${file}`);
}
});
return projects;
}
}
module.exports = new Main();