@easyrn/erpush-cli 0.0.1-alpha

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.

Potentially problematic release.


This version of @easyrn/erpush-cli might be problematic. Click here for more details.

package/src/shell.js ADDED
@@ -0,0 +1,251 @@
1
+ /*
2
+ ______ ____ __
3
+ / ________ ________ __/ __ \__ _______/ /_
4
+ / __/ / __ \/ ___/ / / / /_/ / / / / ___/ __ \
5
+ / /___/ /_/ (__ / /_/ / ____/ /_/ (__ / / / /
6
+ /_____/\__,_/____/\__, /_/ \__,_/____/_/ /_/
7
+ /____/ Version X
8
+ */
9
+ const fs = require('fs');
10
+ const readline = require('readline');
11
+ const {
12
+ supportPlatforms, color, CInfo, CError,
13
+ getPushVersion, getCommonPath, makeTable
14
+ } = require('./utils');
15
+
16
+ class Shell {
17
+ constructor(IO){
18
+ const {stdin, stdout} = IO||{};
19
+ this.stdin = stdin;
20
+ this.stdout = stdout;
21
+ }
22
+
23
+ // 输出字符串
24
+ output(message){
25
+ this.stdout.write(message);
26
+ }
27
+
28
+ // AsciiLogo
29
+ async asciiLogo(pad, output){
30
+ const fileStream = fs.createReadStream(__filename);
31
+ const rl = readline.createInterface({
32
+ input: fileStream,
33
+ crlfDelay: Infinity
34
+ });
35
+ let logo = '';
36
+ let index = 0;
37
+ const prefix = " ".repeat(pad);
38
+ for await (let line of rl) {
39
+ index++;
40
+ if (index < 2) {
41
+ continue;
42
+ }
43
+ let version = '';
44
+ const vIndex = line.indexOf('Version X');
45
+ if (vIndex > -1) {
46
+ version = getPushVersion();
47
+ line = line.substring(0, vIndex);
48
+ version = color('Version '+version, 90);
49
+ }
50
+ logo += prefix + color(line, 33) + version + "\n";
51
+ if (index > 6) {
52
+ break;
53
+ }
54
+ }
55
+ rl.close();
56
+ if (output) {
57
+ this.output(logo);
58
+ }
59
+ }
60
+
61
+ // 获取命令行输入
62
+ async ask(question, completions) {
63
+ const self = this;
64
+ return new Promise(resolve => {
65
+ const rlConfig = {
66
+ input: self.stdin,
67
+ output: self.stdout
68
+ }
69
+ if (completions) {
70
+ if (Array.isArray(completions)) {
71
+ rlConfig.completer = function(prefix) {
72
+ const hits = completions.filter((c) => c.startsWith(prefix));
73
+ return [hits.length ? hits : completions, prefix];
74
+ }
75
+ } else {
76
+ rlConfig.completer = completions;
77
+ }
78
+ }
79
+ const rl = readline.createInterface(rlConfig);
80
+ rl.setPrompt(question);
81
+ rl.prompt();
82
+ rl.on('line', (data) => {
83
+ rl.close();
84
+ resolve(data);
85
+ });
86
+ })
87
+ }
88
+
89
+ // 获取命令行输入的路径, 在输入时可使用 Tab 快速补全
90
+ async askPath(question, rootDir) {
91
+ return await this.ask(question, (prefix) => getCommonPath(rootDir, prefix));
92
+ }
93
+
94
+ // 从命令行或输入获得所选平台
95
+ async getPlatform(platform, required){
96
+ const self = this;
97
+ if (!required && !platform) {
98
+ return null;
99
+ }
100
+ platform = platform || await self.ask("platform:", supportPlatforms);
101
+ if (supportPlatforms.indexOf(platform) == -1) {
102
+ self.output(`${CError}only support (${supportPlatforms.join('/')})\n`);
103
+ return await self.getPlatform(null, true);
104
+ }
105
+ return platform;
106
+ }
107
+
108
+ // 从 list 中选一个 id 序号
109
+ async getRealId(msg, list){
110
+ const self = this;
111
+ let id = await self.ask(msg);
112
+ id = parseInt(id);
113
+ if(typeof id !== "number" || !isFinite(id) || !Math.floor(id) === id) {
114
+ id = null;
115
+ }
116
+ if (id === null || id >= list.length) {
117
+ self.output(`${CError}not exist, please type again\n`)
118
+ return await self.getRealId(msg, list);
119
+ }
120
+ return list[id].id;
121
+ }
122
+
123
+ // 将 datas 数据输出为 Table 格式
124
+ showTable(datas) {
125
+ const self = this;
126
+ if (!datas.length) {
127
+ self.output(`\n${CInfo}No result\n\n`);
128
+ return;
129
+ }
130
+ const keys = Object.keys(datas[0]);
131
+ const list = [keys.map(k => color(k, 32)), '-'];
132
+ datas.forEach(item => {
133
+ const row = [];
134
+ keys.forEach(k => {
135
+ row.push(item[k])
136
+ })
137
+ list.push(row);
138
+ });
139
+ self.output("\n" + makeTable(list) + "\n\n")
140
+ }
141
+
142
+ // 显示 API 错误信息
143
+ showError(result) {
144
+ const self = this;
145
+ const {code, message} = result||{};
146
+ const isNumberCode = typeof code === 'number';
147
+ if (!isNumberCode || code !== 0) {
148
+ self.output(`${CError}[${code}] ${message||'unknown'}\n`);
149
+ return true;
150
+ }
151
+ return false;
152
+ }
153
+
154
+ // 显示 API 返回结果
155
+ showResult(result){
156
+ const self = this;
157
+ if (!self.showError(result)) {
158
+ const message = (result||{}).message;
159
+ self.output(`${CInfo}${message||'success'}\n`);
160
+ }
161
+ return result;
162
+ }
163
+
164
+ // 获取当前命令行的执行函数
165
+ // commands: [string, function, [function], ...]
166
+ // cmd: {name:string, args:Array<string>, usage?:string, showLogo?:bool}
167
+ getCommandsExecuter(commands, cmd){
168
+ // 整理所有命令
169
+ let maxNameLen = 0;
170
+ const forList = [];
171
+ const forSearch = {};
172
+ commands.forEach(item => {
173
+ if ('string' === typeof item) {
174
+ forList.push(item);
175
+ } else {
176
+ const isDef = Array.isArray(item);
177
+ const command = isDef ? item[0] : item;
178
+ let name = command.name;
179
+ maxNameLen = Math.max(maxNameLen, name.length + (isDef ? 2 : 0))
180
+ if (name.indexOf('_') > -1) {
181
+ const [main, sub] = name.split('_');
182
+ if (!forSearch[main]) {
183
+ forSearch[main] = {default:null, list:{}};
184
+ }
185
+ forSearch[main].list[sub] = command;
186
+ if (isDef) {
187
+ forSearch[main].default = command;
188
+ name = main + ' [' + sub + ']';
189
+ } else {
190
+ name = main + ' ' + sub;
191
+ }
192
+ } else {
193
+ forSearch[name] = command;
194
+ }
195
+ forList.push([name, command.options]);
196
+ }
197
+ });
198
+ // 找到可用的命令
199
+ const exec = cmd.name ? cmd.name.toLowerCase() : null;
200
+ if (forSearch[exec]) {
201
+ if (typeof forSearch[exec] === 'function') {
202
+ return forSearch[exec];
203
+ }
204
+ const sub = cmd.args.length ? cmd.args[0].toLowerCase() : null;
205
+ if (!sub && forSearch[exec].default) {
206
+ return forSearch[exec].default;
207
+ }
208
+ if (sub && forSearch[exec].list[sub]) {
209
+ return forSearch[exec].list[sub];
210
+ }
211
+ }
212
+ // 输出所有命令
213
+ const self = this;
214
+ const prefix = " ".repeat(2);
215
+ const cname = (name) => {
216
+ name = prefix + name + " ".repeat(maxNameLen - name.length + 1);
217
+ return color(name, 32, true);
218
+ };
219
+ const cops = (options) => {
220
+ return (options ? color(options, 90) : '') + "\n";
221
+ };
222
+ return async () => {
223
+ if (cmd.showLogo) {
224
+ self.output("\n");
225
+ await self.asciiLogo(1, true);
226
+ }
227
+ if (exec) {
228
+ self.output(`\n Command [${exec}] not found!`);
229
+ }
230
+ self.output(
231
+ "\n Usage: "
232
+ + color(cmd.usage||"erpush <command> [options]", 0, true)
233
+ + "\n\n"
234
+ );
235
+ let firstLine = true;
236
+ forList.forEach(item => {
237
+ if (!Array.isArray(item)) {
238
+ self.output((firstLine ? '' : "\n") + " " + color(item, 33) + "\n");
239
+ } else {
240
+ self.output(cname(item[0]) + cops(item[1]))
241
+ }
242
+ if (firstLine) {
243
+ firstLine = false;
244
+ }
245
+ });
246
+ self.output("\n");
247
+ }
248
+ }
249
+ }
250
+
251
+ module.exports = Shell;