ee-core 2.6.0 → 2.7.0-beta.2
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/config/cache.js +39 -0
- package/config/config.default.js +14 -5
- package/cross/index.js +137 -0
- package/ee/application.js +3 -1
- package/ee/eeApp.js +94 -6
- package/html/cross-failure.html +28 -0
- package/html/failure.html +1 -1
- package/package.json +1 -1
- package/ps/index.js +33 -1
- package/utils/pargv.js +263 -0
package/config/cache.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const EE = require('../ee');
|
|
2
|
+
|
|
3
|
+
const conf = {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 获取 内存中的config
|
|
7
|
+
*/
|
|
8
|
+
_getConfig() {
|
|
9
|
+
const { CoreApp } = EE;
|
|
10
|
+
if (!CoreApp) {
|
|
11
|
+
throw new Error(`[ee-core] [config] Frame initialization is not complete !`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return CoreApp.config;
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* all
|
|
19
|
+
*/
|
|
20
|
+
all() {
|
|
21
|
+
return this._getConfig();
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* getValue
|
|
26
|
+
*/
|
|
27
|
+
getValue(key) {
|
|
28
|
+
const v = this._objectGet(this._getConfig(), key);
|
|
29
|
+
return v;
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
_objectGet(object, path, defaultValue) {
|
|
33
|
+
const pathParts = Array.isArray(path) ? path : path.split('.');
|
|
34
|
+
const value = pathParts.reduce((obj, key) => obj && key in obj ? obj[key] : undefined, object);
|
|
35
|
+
return value === undefined ? defaultValue : value;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
module.exports = conf;
|
package/config/config.default.js
CHANGED
|
@@ -294,11 +294,6 @@ module.exports = appInfo => {
|
|
|
294
294
|
dir: path.join(appInfo.root, 'data'),
|
|
295
295
|
};
|
|
296
296
|
|
|
297
|
-
/**
|
|
298
|
-
* loading页(废弃)
|
|
299
|
-
*/
|
|
300
|
-
config.loadingPage = false;
|
|
301
|
-
|
|
302
297
|
/**
|
|
303
298
|
* addons
|
|
304
299
|
*/
|
|
@@ -317,5 +312,19 @@ module.exports = appInfo => {
|
|
|
317
312
|
rendererExit: true,
|
|
318
313
|
};
|
|
319
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Cross-language service
|
|
317
|
+
* 跨语言服务
|
|
318
|
+
* 例如:执行go的二进制程序
|
|
319
|
+
*/
|
|
320
|
+
config.cross = {};
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* jobs
|
|
324
|
+
*/
|
|
325
|
+
config.jobs = {
|
|
326
|
+
messageLog: true
|
|
327
|
+
};
|
|
328
|
+
|
|
320
329
|
return config;
|
|
321
330
|
};
|
package/cross/index.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const crossSpawn = require('cross-spawn');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const is = require('is-type-of');
|
|
5
|
+
const Conf = require('../config/cache');
|
|
6
|
+
const UtilsHelper = require('../utils/helper');
|
|
7
|
+
const UtilsIs = require('../utils/is');
|
|
8
|
+
const UtilsPargv = require('../utils/pargv');
|
|
9
|
+
const Ps = require('../ps');
|
|
10
|
+
const Log = require('../log');
|
|
11
|
+
const GetPort = require('../utils/get-port');
|
|
12
|
+
const CoreElectronApp = require('../electron/app');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Cross-language service
|
|
16
|
+
* 跨语言服务
|
|
17
|
+
*/
|
|
18
|
+
const CrossLanguageService = {
|
|
19
|
+
|
|
20
|
+
execProcess: {},
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* create
|
|
24
|
+
*/
|
|
25
|
+
async create() {
|
|
26
|
+
if (!Ps.isProd()) {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// init dir
|
|
31
|
+
this._initPath();
|
|
32
|
+
|
|
33
|
+
// boot services
|
|
34
|
+
const servicesCfg = Conf.getValue('cross');
|
|
35
|
+
//await UtilsHelper.sleep(5 * 1000);
|
|
36
|
+
|
|
37
|
+
for (let key of Object.keys(servicesCfg)) {
|
|
38
|
+
let cfg = servicesCfg[key];
|
|
39
|
+
if (cfg.enable == true) {
|
|
40
|
+
this.run(cfg)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* run
|
|
47
|
+
*/
|
|
48
|
+
async run(conf = {}) {
|
|
49
|
+
const cmdName = conf.name;
|
|
50
|
+
const cmdPath = this._getCmdPath(cmdName);
|
|
51
|
+
let cmdArgs = is.string(conf.args) ? [conf.args] : conf.args;
|
|
52
|
+
|
|
53
|
+
// 动态生成port
|
|
54
|
+
// if (Ps.isProd() || this.workspaces.length > 0) {
|
|
55
|
+
let confPort = this.getArgs(cmdArgs, 'port');
|
|
56
|
+
if (!confPort) {
|
|
57
|
+
throw new Error(`[ee-core] [cross/run] --port parameter does not exist!`);
|
|
58
|
+
}
|
|
59
|
+
confPort = await GetPort({ port: confPort });
|
|
60
|
+
// 替换port
|
|
61
|
+
cmdArgs = this.replaceValue(cmdArgs, "--port=", confPort)
|
|
62
|
+
|
|
63
|
+
Log.coreLogger.info(`[ee-core] [cross/run] cmd: ${cmdPath}, args: ${cmdArgs}`);
|
|
64
|
+
|
|
65
|
+
// Launch executable program
|
|
66
|
+
const coreProcess = crossSpawn(cmdPath, cmdArgs, { stdio: 'inherit', detached: false });
|
|
67
|
+
coreProcess.on('close', (code, signal) => {
|
|
68
|
+
Log.coreLogger.info(`[ee-core] [cross/run] [pid=${coreProcess.pid}, port=${port}] exited with code: ${code}, signal: ${signal}`);
|
|
69
|
+
if (0 !== code) {
|
|
70
|
+
// 弹错误窗口
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
CoreElectronApp.quit();
|
|
74
|
+
});
|
|
75
|
+
this.execProcess[cmdName] = coreProcess;
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
getArgs(argv, key) {
|
|
79
|
+
// parse args
|
|
80
|
+
let value = UtilsPargv(argv);
|
|
81
|
+
if (key) {
|
|
82
|
+
value = value[key];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return value;
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
replaceValue(arr, key, value) {
|
|
89
|
+
arr = arr.map(item => {
|
|
90
|
+
if (item.startsWith(key)) {
|
|
91
|
+
let newItem = key + value;
|
|
92
|
+
return newItem;
|
|
93
|
+
} else {
|
|
94
|
+
return item;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
return arr;
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
getUrl(service) {
|
|
101
|
+
const cfg = Conf.getValue('cross');
|
|
102
|
+
const servicesCfg = cfg[service];
|
|
103
|
+
|
|
104
|
+
const args = this.getArgs(servicesCfg.args);
|
|
105
|
+
let protocol = 'http://';
|
|
106
|
+
if (args.hasOwnProperty('ssl') && (args.ssl == 'true' || args.ssl == '1')) {
|
|
107
|
+
protocol = 'https://';
|
|
108
|
+
}
|
|
109
|
+
const hostname = args.hostname ? args.hostname : '127.0.0.1';
|
|
110
|
+
const url = protocol + hostname + ":" + args.port;
|
|
111
|
+
|
|
112
|
+
return url;
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
_getCmdPath(name) {
|
|
116
|
+
const coreName = UtilsIs.windows() ? name + ".exe" : name;
|
|
117
|
+
const p = path.join(Ps.getExtraResourcesDir(), coreName);
|
|
118
|
+
return p;
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* init path
|
|
123
|
+
*/
|
|
124
|
+
_initPath() {
|
|
125
|
+
try {
|
|
126
|
+
const pathname = Ps.getUserHomeConfigDir();
|
|
127
|
+
if (!fs.existsSync(pathname)) {
|
|
128
|
+
UtilsHelper.mkdir(pathname, {mode: 0o755});
|
|
129
|
+
}
|
|
130
|
+
} catch (e) {
|
|
131
|
+
Log.coreLogger.error(e);
|
|
132
|
+
throw new Error(`[ee-core] [cross] mkdir ${pathname} failed !`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = CrossLanguageService;
|
package/ee/application.js
CHANGED
|
@@ -57,7 +57,7 @@ class Appliaction extends EeApp {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// normalize env
|
|
60
|
-
|
|
60
|
+
env.EE_APP_NAME = options.appName;
|
|
61
61
|
env.EE_HOME = options.homeDir;
|
|
62
62
|
env.EE_BASE_DIR = options.baseDir;
|
|
63
63
|
env.EE_SERVER_ENV = options.env;
|
|
@@ -89,6 +89,8 @@ class Appliaction extends EeApp {
|
|
|
89
89
|
|
|
90
90
|
await this.startSocket();
|
|
91
91
|
|
|
92
|
+
await this.crossService();
|
|
93
|
+
|
|
92
94
|
await this.ready();
|
|
93
95
|
|
|
94
96
|
await this.createElectronApp();
|
package/ee/eeApp.js
CHANGED
|
@@ -16,6 +16,7 @@ const Socket = require('../socket');
|
|
|
16
16
|
const GetPort = require('../utils/get-port');
|
|
17
17
|
const UtilsHelper = require('../utils/helper');
|
|
18
18
|
const HttpClient = require('../httpclient');
|
|
19
|
+
const Cross = require('../cross');
|
|
19
20
|
|
|
20
21
|
class EeApp extends BaseApp {
|
|
21
22
|
constructor(options = {}) {
|
|
@@ -59,6 +60,13 @@ class EeApp extends BaseApp {
|
|
|
59
60
|
Socket.startAll(this);
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
/**
|
|
64
|
+
* 启动跨语言服务
|
|
65
|
+
*/
|
|
66
|
+
async crossService() {
|
|
67
|
+
Cross.create();
|
|
68
|
+
}
|
|
69
|
+
|
|
62
70
|
/**
|
|
63
71
|
* 创建electron应用
|
|
64
72
|
*/
|
|
@@ -105,18 +113,21 @@ class EeApp extends BaseApp {
|
|
|
105
113
|
return;
|
|
106
114
|
}
|
|
107
115
|
|
|
116
|
+
const mainServer = this.config.mainServer;
|
|
117
|
+
|
|
108
118
|
// 开发环境
|
|
109
119
|
if (Ps.isDev()) {
|
|
110
120
|
let modeInfo;
|
|
111
121
|
let url;
|
|
112
122
|
let load = 'url';
|
|
113
123
|
const configFile = './electron/config/bin.js';
|
|
124
|
+
let electronCfg = {};
|
|
114
125
|
|
|
115
126
|
const isBin = UtilsHelper.checkConfig(configFile);
|
|
116
127
|
if (isBin) {
|
|
117
128
|
const binConfig = UtilsHelper.loadConfig(configFile);
|
|
118
|
-
|
|
119
|
-
|
|
129
|
+
modeInfo = binConfig.dev.frontend;
|
|
130
|
+
electronCfg = binConfig.dev.electron;
|
|
120
131
|
} else {
|
|
121
132
|
// 兼容旧的 developmentMode
|
|
122
133
|
const developmentModeConfig = this.config.developmentMode;
|
|
@@ -132,12 +143,17 @@ class EeApp extends BaseApp {
|
|
|
132
143
|
|
|
133
144
|
// 检查 UI serve是否启动,先加载一个boot page
|
|
134
145
|
if (load == 'url') {
|
|
135
|
-
|
|
136
|
-
|
|
146
|
+
// loading page
|
|
147
|
+
let lp = path.join(__dirname, '..', 'html', 'boot.html');
|
|
148
|
+
if (electronCfg.hasOwnProperty('loadingPage') && electronCfg.loadingPage != '') {
|
|
149
|
+
lp = path.join(this.config.homeDir, electronCfg.loadingPage);
|
|
150
|
+
}
|
|
151
|
+
this._loadingPage(lp);
|
|
152
|
+
|
|
137
153
|
let count = 0;
|
|
138
154
|
let frontendReady = false;
|
|
139
155
|
const hc = new HttpClient();
|
|
140
|
-
while(!frontendReady && count <
|
|
156
|
+
while(!frontendReady && count < 60){
|
|
141
157
|
await UtilsHelper.sleep(1 * 1000);
|
|
142
158
|
try {
|
|
143
159
|
await hc.request(url, {
|
|
@@ -165,7 +181,13 @@ class EeApp extends BaseApp {
|
|
|
165
181
|
}
|
|
166
182
|
|
|
167
183
|
// 生产环境
|
|
168
|
-
|
|
184
|
+
// cross service takeover web
|
|
185
|
+
if (mainServer.hasOwnProperty('takeover')) {
|
|
186
|
+
await this._crossTakeover(mainServer)
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 主进程
|
|
169
191
|
if (Conf.isFileProtocol(mainServer)) {
|
|
170
192
|
url = path.join(this.config.homeDir, mainServer.indexPath);
|
|
171
193
|
this.loadMainUrl('spa', url, 'file');
|
|
@@ -174,6 +196,61 @@ class EeApp extends BaseApp {
|
|
|
174
196
|
}
|
|
175
197
|
}
|
|
176
198
|
|
|
199
|
+
/**
|
|
200
|
+
* cross service takeover web
|
|
201
|
+
*/
|
|
202
|
+
async _crossTakeover(mainCfg = {}) {
|
|
203
|
+
const crossConfig = this.config.cross;
|
|
204
|
+
|
|
205
|
+
// loading page
|
|
206
|
+
if (mainCfg.hasOwnProperty('loadingPage')) {
|
|
207
|
+
const lp = path.join(this.config.homeDir, mainCfg.loadingPage);
|
|
208
|
+
this._loadingPage(lp);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// cross service url
|
|
212
|
+
const service = mainCfg.takeover;
|
|
213
|
+
if (!crossConfig.hasOwnProperty(service)) {
|
|
214
|
+
throw new Error(`[ee-core] Please Check the value of mainServer.takeover in the config file !`);
|
|
215
|
+
}
|
|
216
|
+
// check service
|
|
217
|
+
if (crossConfig[service].enable != true) {
|
|
218
|
+
throw new Error(`[ee-core] Please Check the value of cross.${service} enable is true !`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const url = Cross.getUrl(service);
|
|
222
|
+
|
|
223
|
+
let count = 0;
|
|
224
|
+
let serviceReady = false;
|
|
225
|
+
const hc = new HttpClient();
|
|
226
|
+
|
|
227
|
+
// 循环检查
|
|
228
|
+
const times = Ps.isDev() ? 20 : 100;
|
|
229
|
+
const sleeptime = Ps.isDev() ? 1000 : 100;
|
|
230
|
+
while(!serviceReady && count < times){
|
|
231
|
+
await UtilsHelper.sleep(sleeptime);
|
|
232
|
+
try {
|
|
233
|
+
await hc.request(url, {
|
|
234
|
+
method: 'GET',
|
|
235
|
+
timeout: 100,
|
|
236
|
+
});
|
|
237
|
+
serviceReady = true;
|
|
238
|
+
} catch(err) {
|
|
239
|
+
//console.log('The cross service is starting');
|
|
240
|
+
}
|
|
241
|
+
count++;
|
|
242
|
+
}
|
|
243
|
+
//console.log('count:', count)
|
|
244
|
+
if (serviceReady == false) {
|
|
245
|
+
const bootFailurePage = path.join(__dirname, '..', 'html', 'cross-failure.html');
|
|
246
|
+
this.mainWindow.loadFile(bootFailurePage);
|
|
247
|
+
throw new Error(`[ee-core] Please check cross service [${service}] ${url} !`)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
Log.coreLogger.info(`[ee-core] cross service [${service}] is started successfully`);
|
|
251
|
+
this.loadMainUrl('spa', url);
|
|
252
|
+
}
|
|
253
|
+
|
|
177
254
|
/**
|
|
178
255
|
* 加载本地前端资源
|
|
179
256
|
*/
|
|
@@ -241,6 +318,17 @@ class EeApp extends BaseApp {
|
|
|
241
318
|
}
|
|
242
319
|
}
|
|
243
320
|
|
|
321
|
+
/**
|
|
322
|
+
* loading page
|
|
323
|
+
*/
|
|
324
|
+
_loadingPage(name) {
|
|
325
|
+
if (!fs.existsSync(name)) {
|
|
326
|
+
return
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
this.mainWindow.loadFile(name);
|
|
330
|
+
}
|
|
331
|
+
|
|
244
332
|
/**
|
|
245
333
|
* electron app退出
|
|
246
334
|
*/
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0, minimum-scale=1.0" />
|
|
7
|
+
<title>Booting failure</title>
|
|
8
|
+
<style>
|
|
9
|
+
#failure {
|
|
10
|
+
background-color: #dedede;
|
|
11
|
+
font-size: 14px;
|
|
12
|
+
}
|
|
13
|
+
.base {
|
|
14
|
+
position: absolute;
|
|
15
|
+
top: 50%;
|
|
16
|
+
left: 50%;
|
|
17
|
+
transform: translate(-50%, -50%);
|
|
18
|
+
}
|
|
19
|
+
</style>
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
<div id="failure">
|
|
23
|
+
<div class='base'>
|
|
24
|
+
Booting failure, please check cross service is runing !
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</body>
|
|
28
|
+
</html>
|
package/html/failure.html
CHANGED
package/package.json
CHANGED
package/ps/index.js
CHANGED
|
@@ -127,6 +127,13 @@ exports.processType = function() {
|
|
|
127
127
|
return type;
|
|
128
128
|
};
|
|
129
129
|
|
|
130
|
+
/**
|
|
131
|
+
* app name
|
|
132
|
+
*/
|
|
133
|
+
exports.appName = function() {
|
|
134
|
+
return process.env.EE_APP_NAME;
|
|
135
|
+
}
|
|
136
|
+
|
|
130
137
|
/**
|
|
131
138
|
* 获取home路径
|
|
132
139
|
*/
|
|
@@ -230,10 +237,35 @@ exports.getExecDir = function() {
|
|
|
230
237
|
/**
|
|
231
238
|
* 获取操作系统用户目录
|
|
232
239
|
*/
|
|
233
|
-
exports.getUserHomeDir = function
|
|
240
|
+
exports.getUserHomeDir = function() {
|
|
234
241
|
return process.env.EE_USER_HOME;
|
|
235
242
|
}
|
|
236
243
|
|
|
244
|
+
/**
|
|
245
|
+
* 获取用户目录配置数据目录
|
|
246
|
+
*/
|
|
247
|
+
exports.getUserHomeConfigDir = function() {
|
|
248
|
+
// const filePath = path.join(this.getHomeDir(), 'package.json');
|
|
249
|
+
// if (!fs.existsSync(filePath)) {
|
|
250
|
+
// throw new Error(filePath + ' is not found');
|
|
251
|
+
// }
|
|
252
|
+
// const pkg = JSON.parse(fs.readFileSync(filePath));
|
|
253
|
+
// if (!pkg.name || pkg.name == "") {
|
|
254
|
+
// throw new Error(`name is required from ${filePath}`);
|
|
255
|
+
// }
|
|
256
|
+
const appname = this.appName();
|
|
257
|
+
const cfgDir = path.join(this.getUserHomeDir(), ".config", appname);
|
|
258
|
+
return cfgDir;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* 获取基础数据路径
|
|
263
|
+
*/
|
|
264
|
+
exports.getUserHomeAppFilePath = function() {
|
|
265
|
+
const p = path.join(this.getUserHomeConfigDir(), "app.json");
|
|
266
|
+
return p;
|
|
267
|
+
}
|
|
268
|
+
|
|
237
269
|
/**
|
|
238
270
|
* 获取主进程端口
|
|
239
271
|
*/
|
package/utils/pargv.js
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function hasKey(obj, keys) {
|
|
4
|
+
var o = obj;
|
|
5
|
+
keys.slice(0, -1).forEach(function (key) {
|
|
6
|
+
o = o[key] || {};
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
var key = keys[keys.length - 1];
|
|
10
|
+
return key in o;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isNumber(x) {
|
|
14
|
+
if (typeof x === 'number') { return true; }
|
|
15
|
+
if ((/^0x[0-9a-f]+$/i).test(x)) { return true; }
|
|
16
|
+
return (/^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/).test(x);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isConstructorOrProto(obj, key) {
|
|
20
|
+
return (key === 'constructor' && typeof obj[key] === 'function') || key === '__proto__';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = function (args, opts) {
|
|
24
|
+
if (!opts) { opts = {}; }
|
|
25
|
+
|
|
26
|
+
var flags = {
|
|
27
|
+
bools: {},
|
|
28
|
+
strings: {},
|
|
29
|
+
unknownFn: null,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
if (typeof opts.unknown === 'function') {
|
|
33
|
+
flags.unknownFn = opts.unknown;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (typeof opts.boolean === 'boolean' && opts.boolean) {
|
|
37
|
+
flags.allBools = true;
|
|
38
|
+
} else {
|
|
39
|
+
[].concat(opts.boolean).filter(Boolean).forEach(function (key) {
|
|
40
|
+
flags.bools[key] = true;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
var aliases = {};
|
|
45
|
+
|
|
46
|
+
function aliasIsBoolean(key) {
|
|
47
|
+
return aliases[key].some(function (x) {
|
|
48
|
+
return flags.bools[x];
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
Object.keys(opts.alias || {}).forEach(function (key) {
|
|
53
|
+
aliases[key] = [].concat(opts.alias[key]);
|
|
54
|
+
aliases[key].forEach(function (x) {
|
|
55
|
+
aliases[x] = [key].concat(aliases[key].filter(function (y) {
|
|
56
|
+
return x !== y;
|
|
57
|
+
}));
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
[].concat(opts.string).filter(Boolean).forEach(function (key) {
|
|
62
|
+
flags.strings[key] = true;
|
|
63
|
+
if (aliases[key]) {
|
|
64
|
+
[].concat(aliases[key]).forEach(function (k) {
|
|
65
|
+
flags.strings[k] = true;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
var defaults = opts.default || {};
|
|
71
|
+
|
|
72
|
+
var argv = { _: [] };
|
|
73
|
+
|
|
74
|
+
function argDefined(key, arg) {
|
|
75
|
+
return (flags.allBools && (/^--[^=]+$/).test(arg))
|
|
76
|
+
|| flags.strings[key]
|
|
77
|
+
|| flags.bools[key]
|
|
78
|
+
|| aliases[key];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function setKey(obj, keys, value) {
|
|
82
|
+
var o = obj;
|
|
83
|
+
for (var i = 0; i < keys.length - 1; i++) {
|
|
84
|
+
var key = keys[i];
|
|
85
|
+
if (isConstructorOrProto(o, key)) { return; }
|
|
86
|
+
if (o[key] === undefined) { o[key] = {}; }
|
|
87
|
+
if (
|
|
88
|
+
o[key] === Object.prototype
|
|
89
|
+
|| o[key] === Number.prototype
|
|
90
|
+
|| o[key] === String.prototype
|
|
91
|
+
) {
|
|
92
|
+
o[key] = {};
|
|
93
|
+
}
|
|
94
|
+
if (o[key] === Array.prototype) { o[key] = []; }
|
|
95
|
+
o = o[key];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
var lastKey = keys[keys.length - 1];
|
|
99
|
+
if (isConstructorOrProto(o, lastKey)) { return; }
|
|
100
|
+
if (
|
|
101
|
+
o === Object.prototype
|
|
102
|
+
|| o === Number.prototype
|
|
103
|
+
|| o === String.prototype
|
|
104
|
+
) {
|
|
105
|
+
o = {};
|
|
106
|
+
}
|
|
107
|
+
if (o === Array.prototype) { o = []; }
|
|
108
|
+
if (o[lastKey] === undefined || flags.bools[lastKey] || typeof o[lastKey] === 'boolean') {
|
|
109
|
+
o[lastKey] = value;
|
|
110
|
+
} else if (Array.isArray(o[lastKey])) {
|
|
111
|
+
o[lastKey].push(value);
|
|
112
|
+
} else {
|
|
113
|
+
o[lastKey] = [o[lastKey], value];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function setArg(key, val, arg) {
|
|
118
|
+
if (arg && flags.unknownFn && !argDefined(key, arg)) {
|
|
119
|
+
if (flags.unknownFn(arg) === false) { return; }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
var value = !flags.strings[key] && isNumber(val)
|
|
123
|
+
? Number(val)
|
|
124
|
+
: val;
|
|
125
|
+
setKey(argv, key.split('.'), value);
|
|
126
|
+
|
|
127
|
+
(aliases[key] || []).forEach(function (x) {
|
|
128
|
+
setKey(argv, x.split('.'), value);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
Object.keys(flags.bools).forEach(function (key) {
|
|
133
|
+
setArg(key, defaults[key] === undefined ? false : defaults[key]);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
var notFlags = [];
|
|
137
|
+
|
|
138
|
+
if (args.indexOf('--') !== -1) {
|
|
139
|
+
notFlags = args.slice(args.indexOf('--') + 1);
|
|
140
|
+
args = args.slice(0, args.indexOf('--'));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for (var i = 0; i < args.length; i++) {
|
|
144
|
+
var arg = args[i];
|
|
145
|
+
var key;
|
|
146
|
+
var next;
|
|
147
|
+
|
|
148
|
+
if ((/^--.+=/).test(arg)) {
|
|
149
|
+
// Using [\s\S] instead of . because js doesn't support the
|
|
150
|
+
// 'dotall' regex modifier. See:
|
|
151
|
+
// http://stackoverflow.com/a/1068308/13216
|
|
152
|
+
var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
|
|
153
|
+
key = m[1];
|
|
154
|
+
var value = m[2];
|
|
155
|
+
if (flags.bools[key]) {
|
|
156
|
+
value = value !== 'false';
|
|
157
|
+
}
|
|
158
|
+
setArg(key, value, arg);
|
|
159
|
+
} else if ((/^--no-.+/).test(arg)) {
|
|
160
|
+
key = arg.match(/^--no-(.+)/)[1];
|
|
161
|
+
setArg(key, false, arg);
|
|
162
|
+
} else if ((/^--.+/).test(arg)) {
|
|
163
|
+
key = arg.match(/^--(.+)/)[1];
|
|
164
|
+
next = args[i + 1];
|
|
165
|
+
if (
|
|
166
|
+
next !== undefined
|
|
167
|
+
&& !(/^(-|--)[^-]/).test(next)
|
|
168
|
+
&& !flags.bools[key]
|
|
169
|
+
&& !flags.allBools
|
|
170
|
+
&& (aliases[key] ? !aliasIsBoolean(key) : true)
|
|
171
|
+
) {
|
|
172
|
+
setArg(key, next, arg);
|
|
173
|
+
i += 1;
|
|
174
|
+
} else if ((/^(true|false)$/).test(next)) {
|
|
175
|
+
setArg(key, next === 'true', arg);
|
|
176
|
+
i += 1;
|
|
177
|
+
} else {
|
|
178
|
+
setArg(key, flags.strings[key] ? '' : true, arg);
|
|
179
|
+
}
|
|
180
|
+
} else if ((/^-[^-]+/).test(arg)) {
|
|
181
|
+
var letters = arg.slice(1, -1).split('');
|
|
182
|
+
|
|
183
|
+
var broken = false;
|
|
184
|
+
for (var j = 0; j < letters.length; j++) {
|
|
185
|
+
next = arg.slice(j + 2);
|
|
186
|
+
|
|
187
|
+
if (next === '-') {
|
|
188
|
+
setArg(letters[j], next, arg);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if ((/[A-Za-z]/).test(letters[j]) && next[0] === '=') {
|
|
193
|
+
setArg(letters[j], next.slice(1), arg);
|
|
194
|
+
broken = true;
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (
|
|
199
|
+
(/[A-Za-z]/).test(letters[j])
|
|
200
|
+
&& (/-?\d+(\.\d*)?(e-?\d+)?$/).test(next)
|
|
201
|
+
) {
|
|
202
|
+
setArg(letters[j], next, arg);
|
|
203
|
+
broken = true;
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (letters[j + 1] && letters[j + 1].match(/\W/)) {
|
|
208
|
+
setArg(letters[j], arg.slice(j + 2), arg);
|
|
209
|
+
broken = true;
|
|
210
|
+
break;
|
|
211
|
+
} else {
|
|
212
|
+
setArg(letters[j], flags.strings[letters[j]] ? '' : true, arg);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
key = arg.slice(-1)[0];
|
|
217
|
+
if (!broken && key !== '-') {
|
|
218
|
+
if (
|
|
219
|
+
args[i + 1]
|
|
220
|
+
&& !(/^(-|--)[^-]/).test(args[i + 1])
|
|
221
|
+
&& !flags.bools[key]
|
|
222
|
+
&& (aliases[key] ? !aliasIsBoolean(key) : true)
|
|
223
|
+
) {
|
|
224
|
+
setArg(key, args[i + 1], arg);
|
|
225
|
+
i += 1;
|
|
226
|
+
} else if (args[i + 1] && (/^(true|false)$/).test(args[i + 1])) {
|
|
227
|
+
setArg(key, args[i + 1] === 'true', arg);
|
|
228
|
+
i += 1;
|
|
229
|
+
} else {
|
|
230
|
+
setArg(key, flags.strings[key] ? '' : true, arg);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
if (!flags.unknownFn || flags.unknownFn(arg) !== false) {
|
|
235
|
+
argv._.push(flags.strings._ || !isNumber(arg) ? arg : Number(arg));
|
|
236
|
+
}
|
|
237
|
+
if (opts.stopEarly) {
|
|
238
|
+
argv._.push.apply(argv._, args.slice(i + 1));
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
Object.keys(defaults).forEach(function (k) {
|
|
245
|
+
if (!hasKey(argv, k.split('.'))) {
|
|
246
|
+
setKey(argv, k.split('.'), defaults[k]);
|
|
247
|
+
|
|
248
|
+
(aliases[k] || []).forEach(function (x) {
|
|
249
|
+
setKey(argv, x.split('.'), defaults[k]);
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
if (opts['--']) {
|
|
255
|
+
argv['--'] = notFlags.slice();
|
|
256
|
+
} else {
|
|
257
|
+
notFlags.forEach(function (k) {
|
|
258
|
+
argv._.push(k);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return argv;
|
|
263
|
+
};
|