erii 2.0.6 → 3.0.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/src/index.ts DELETED
@@ -1,394 +0,0 @@
1
- const yargs = require('yargs-parser');
2
- const CLI = require('clui'), clc = require('cli-color');
3
- const chalk = require('chalk');
4
- const validator = require('validator');
5
- import { createCamelProxifiedObject } from "./utils/convert";
6
- export interface Command {
7
- name: string | string[];
8
- description?: string;
9
- argument?: Argument,
10
- alias?: string[],
11
- redirect?: string;
12
- options?: Option[];
13
- handler?: (ctx: CommandCtx, options?: object) => any
14
- }
15
-
16
- export interface Option {
17
- name: string | string[];
18
- description?: string;
19
- command?: string;
20
- argument?: Argument,
21
- }
22
-
23
- export interface CommandMap {
24
- [key: string]: Command;
25
- };
26
-
27
- export interface CommandCtx {
28
- showVersion: () => void;
29
- showHelp: () => void;
30
- getArgument: (commandName?: string) => string;
31
- }
32
-
33
- export interface Argument {
34
- name: string;
35
- description: string;
36
- validate?: string | ((value: any, logger: (message: string) => void) => boolean);
37
- }
38
-
39
- export class Erii {
40
- rawArguments: string[];
41
- parsedArguments: {
42
- _?: string[],
43
- [key: string]: string | string[];
44
- } = {};
45
- private version: string = "1.0.0";
46
- private name: string = "Erii";
47
-
48
- commands: CommandMap = {};
49
- commonOptions: Option[] = [];
50
- validator: any;
51
- alwaysHandler: () => void;
52
- defaultHandler: () => void;
53
-
54
- constructor() {
55
- this.rawArguments = process.argv.slice(2);
56
- this.parsedArguments = yargs(process.argv.slice(2), {
57
- configuration: {
58
- "boolean-negation": false
59
- }
60
- });
61
- this.validator = validator;
62
-
63
- // 不响应-后第二个字符起的命令
64
- for (const key of Object.keys(this.parsedArguments)) {
65
- if (key !== "_" && (!this.rawArguments.includes('--' + key) && !this.rawArguments.includes('-' + key))) {
66
- delete this.parsedArguments[key];
67
- }
68
- }
69
- }
70
-
71
- /**
72
- * 绑定命令处理函数
73
- * @param config
74
- * @param handler
75
- */
76
- bind(config: Command, handler: (ctx: CommandCtx, ...extraArguments) => any) {
77
- if (config.name === undefined) {
78
- return console.error(chalk.red("Invalid command binding, ignored."));
79
- }
80
-
81
- const { name, description, argument } = config;
82
-
83
- const mainCommand = Array.isArray(name) ? name.shift() : name;
84
-
85
- this.commands[mainCommand] = {
86
- name: mainCommand,
87
- description,
88
- argument,
89
- alias: [],
90
- options: [],
91
- handler
92
- };
93
-
94
- if (Array.isArray(name)) {
95
- for (const alias of name) {
96
- this.commands[mainCommand].alias.push(alias);
97
- this.commands[alias] = {
98
- name: alias,
99
- redirect: mainCommand
100
- };
101
- }
102
- }
103
- }
104
-
105
- /**
106
- * 总是执行
107
- * @param handler
108
- */
109
- always(handler: () => any) {
110
- this.alwaysHandler = handler;
111
- }
112
-
113
- /**
114
- *
115
- */
116
- default(handler: () => any) {
117
- this.defaultHandler = handler;
118
- }
119
-
120
- /**
121
- * 增加设置项
122
- * @param config
123
- */
124
- addOption(config: Option) {
125
- config.name = Array.isArray(config.name) ? config.name : [config.name];
126
- if (!config.command) {
127
- this.commonOptions.push(config);
128
- } else {
129
- if (!(config.command in this.commands)){
130
- return console.error(chalk.red(`Command for option [${config.name.join(', ')}] not found, ignored.`));
131
- }
132
- this.commands[config.command].options.push(config);
133
- }
134
- }
135
-
136
- /**
137
- *
138
- * @param command
139
- */
140
- private commandCtx(command: string): CommandCtx {
141
- return {
142
- showVersion: () => {
143
- this.showVersion();
144
- },
145
- showHelp: () => {
146
- this.showHelp();
147
- },
148
- getArgument: (commandName: string = command) => {
149
- return this.getArgument(commandName);
150
- }
151
- }
152
- }
153
-
154
- /**
155
- * 设定基础信息
156
- * @param metaInfo
157
- */
158
- setMetaInfo({
159
- version = "",
160
- name = ""
161
- } = {}) {
162
- this.version = version;
163
- this.name = name;
164
- }
165
-
166
- /**
167
- * 显示帮助信息
168
- */
169
- showHelp(command?: string) {
170
- this.showVersion();
171
- console.log('\nHelp:');
172
- const Line = CLI.Line;
173
-
174
- new Line()
175
- .padding(5)
176
- .column('Commands', 30, [clc.cyan])
177
- .column('Description', 30, [clc.cyan])
178
- .column('Alias', 20, [clc.cyan])
179
- .output();
180
- new Line().output();
181
- for (const key of Object.keys(this.commands)) {
182
- if (this.commands[key].redirect) {
183
- continue;
184
- }
185
- let commandText = '--' + key;
186
- let aliasText = '';
187
- if (this.commands[key].argument) {
188
- commandText += ` <${this.commands[key].argument.name}>`;
189
- }
190
- if (this.commands[key].alias.length > 0) {
191
- const aliasWithPrefixes = this.commands[key].alias.map(alias => alias.length === 1 ? `-${alias}` : `--${alias}`);
192
- aliasText += aliasWithPrefixes.join(' / ');
193
- }
194
- new Line()
195
- .padding(5)
196
- .column(commandText, 30)
197
- .column(this.commands[key].description, 30)
198
- .column(aliasText, 20)
199
- .output();
200
- if (this.commands[key].argument) {
201
- // 参数说明
202
- new Line()
203
- .padding(9)
204
- .column(`<${this.commands[key].argument.name}>`, 26)
205
- .column(this.commands[key].argument.description, 30)
206
- .output();
207
- }
208
- if (this.commands[key].options.length > 0) {
209
- // 设置项说明
210
- for (const option of this.commands[key].options) {
211
- let optionsText = '';
212
- const optionName = Array.isArray(option.name) ? option.name : [option.name];
213
- if (option.argument) {
214
- optionsText += `--${optionName.join(', ')} <${option.argument.name}>`;
215
- } else {
216
- optionsText += `--${optionName.join(', ')}`
217
- };
218
- new Line()
219
- .padding(9)
220
- .column(optionsText, 26)
221
- .column(option.description || '')
222
- .output();
223
- if (option.argument) {
224
- // 设置项参数说明
225
- new Line()
226
- .padding(13)
227
- .column(`<${option.argument.name}>`, 22)
228
- .column(option.argument.description || '')
229
- .output();
230
- }
231
- }
232
- }
233
- }
234
- new Line().output();
235
- if (this.commonOptions.length > 0) {
236
- // 通用设置项说明
237
- console.log('Options:\n');
238
- new Line()
239
- .padding(5)
240
- .column('Options', 30, [clc.cyan])
241
- .column('Description', 30, [clc.cyan])
242
- .output();
243
- for (const option of this.commonOptions) {
244
- let optionsText = '';
245
- const optionName = Array.isArray(option.name) ? option.name : [option.name];
246
- if (option.argument) {
247
- optionsText += `--${optionName.join(', ')} <${option.argument.name}>`;
248
- } else {
249
- optionsText += `--${optionName.join(', ')}`
250
- };
251
- new Line()
252
- .padding(5)
253
- .column(optionsText, 30)
254
- .column(option.description || '')
255
- .output();
256
- if (option.argument) {
257
- // 设置项参数说明
258
- new Line()
259
- .padding(9)
260
- .column(`<${option.argument.name}>`, 26)
261
- .column(option.argument.description || '')
262
- .output();
263
- }
264
- }
265
- }
266
- }
267
-
268
- /**
269
- * 显示版本号
270
- */
271
- showVersion() {
272
- console.log(`${this.name} / ${this.version}`);
273
- }
274
- /**
275
- * 启动
276
- */
277
- start() {
278
- if (this.alwaysHandler) {
279
- this.alwaysHandler();
280
- }
281
- if (this.defaultHandler) {
282
- if (this.parsedArguments['_'].length === 0 && Object.keys(this.parsedArguments).length === 1) {
283
- this.defaultHandler();
284
- }
285
- }
286
- for (const key of Object.keys(this.parsedArguments)) {
287
- if (key in this.commands) {
288
- this.exec(key);
289
- }
290
- }
291
- for (const key of this.parsedArguments['_']) {
292
- if (key in this.commands) {
293
- this.exec(key);
294
- }
295
- }
296
- }
297
-
298
- /**
299
- * 执行命令担当函数
300
- * @param command
301
- * @param extraArguments
302
- */
303
- private exec(command) {
304
- if (this.commands[command].redirect) {
305
- // 别名重定向
306
- this.exec(this.commands[command].redirect);
307
- } else {
308
- // 传递绑定的设置及通用设置
309
- const options = {};
310
- const bandOptions = this.commands[command].options.concat(this.commonOptions);
311
- for (const option of bandOptions) {
312
- for (const name of option.name) {
313
- if (name in this.parsedArguments) {
314
- // do option argument validation
315
- if (this.validateArgument(this.parsedArguments[name], option.argument)) {
316
- options[option.name[0]] = this.parsedArguments[name];
317
- } else {
318
- console.error(chalk.red(`Argument validation failed for option '${name}'.`));
319
- if (typeof option.argument.validate === 'string') {
320
- console.error(chalk.red(`<${option.argument.name}> should be a/an ${option.argument.validate.slice(2)}.`))
321
- }
322
- }
323
- }
324
- }
325
- }
326
- // do command argument validation
327
- if (this.validateArgument(this.parsedArguments[command], this.commands[command].argument)) {
328
- this.commands[command].handler(this.commandCtx(command), createCamelProxifiedObject(options));
329
- } else {
330
- console.error(chalk.red(`Argument validation failed for command ${command}`));
331
- const validateMethod = this.commands[command].argument.validate;
332
- if (typeof validateMethod === 'string') {
333
- console.error(chalk.red(`<${this.commands[command].argument.name}> should be a/an ${validateMethod.slice(2)}.`))
334
- }
335
- }
336
- }
337
- }
338
-
339
- validateArgument(argumentValue: any, argument: Argument) {
340
- if (!argument || !argument.validate) {
341
- // no need to validate
342
- return true;
343
- }
344
- if (typeof argument.validate === "string") {
345
- if (argument.validate in this.validator) {
346
- return this.validator[argument.validate](argumentValue.toString());
347
- } else {
348
- console.error(chalk.red(`Unknown validate method for ${argument.name}.`));
349
- return true;
350
- }
351
- } else {
352
- // custom validator
353
- return argument.validate(argumentValue, (message: string) => {
354
- console.log(chalk.red(message))
355
- });
356
- }
357
- }
358
-
359
- /**
360
- * 获得命令的参数
361
- * @param commandName
362
- * @param followRedirect 是否遵循重定向
363
- */
364
- getArgument(commandName: string, followRedirect = true): string {
365
- if (this.commands[commandName]) {
366
- if (this.commands[commandName].redirect) {
367
- return this.getArgument(this.commands[commandName].redirect);
368
- } else {
369
- if (Object.keys(this.parsedArguments).includes(commandName) && commandName !== '_') {
370
- return this.parsedArguments[commandName] as string;
371
- } else {
372
- for (const alias of this.commands[commandName].alias) {
373
- if (Object.keys(this.parsedArguments).includes(alias)) {
374
- return this.parsedArguments[alias] as string;
375
- }
376
- }
377
- console.error(chalk.red(`Command ${commandName} not found.`));
378
- }
379
- }
380
- } else {
381
- console.error(chalk.red(`Command ${commandName} not found.`));
382
- }
383
- }
384
-
385
- /**
386
- * 启动
387
- * エリイ 起きてます❤
388
- */
389
- okite() {
390
- return this.start();
391
- }
392
- }
393
-
394
- export default new Erii();
@@ -1,44 +0,0 @@
1
- export function toCamelCase(str: string) {
2
- return str.replace(/([-_][a-z])/gi, ($1) => {
3
- return $1.toUpperCase().replace("-", "");
4
- });
5
- }
6
-
7
- export function toKebabCase(str: string) {
8
- return (
9
- str[0].toLowerCase() +
10
- str
11
- .slice(1, str.length)
12
- .replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)
13
- );
14
- }
15
-
16
- export function convertObjectKey(before: { [index: string]: any }) {
17
- return Object.fromEntries(
18
- Object.entries(before).map(([key, value]) => [toCamelCase(key), value])
19
- );
20
- }
21
-
22
- export function createCamelProxifiedObject(before: { [index: string]: any }) {
23
- const originalObject = before;
24
- return new Proxy(before, {
25
- get: (target: typeof before, key: string) => {
26
- return originalObject[key] ?? originalObject[toKebabCase(key)];
27
- },
28
- set: (target: typeof before, key: string, value: any) => {
29
- // 如果赋值的是camel case的key,而驼峰形式key是从kebab case的key proxy来的 则赋值其kebab case形式的key的值
30
- if (
31
- !key.includes("-") &&
32
- (originalObject[key] === undefined ||
33
- originalObject[key] === null) &&
34
- originalObject[toKebabCase(key)] !== undefined &&
35
- originalObject[toKebabCase(key)] !== null
36
- ) {
37
- target[toKebabCase(key)] = value;
38
- } else {
39
- target[key] = value;
40
- }
41
- return value;
42
- },
43
- });
44
- }
package/tsconfig.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es6",
4
- "outDir": "dist",
5
- "declaration": true,
6
- "module": "commonjs",
7
- "sourceMap": true,
8
- "lib": ["es7", "esnext"],
9
- "experimentalDecorators": true,
10
- "newLine": "lf"
11
- },
12
- "include": ["src/**/*"],
13
- "exclude": ["dist"]
14
- }