@noravel/command 1.0.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/LICENSE +9 -0
- package/README.md +569 -0
- package/dist/Command.d.ts +27 -0
- package/dist/Command.js +86 -0
- package/dist/Contracts/Command.d.ts +10 -0
- package/dist/Contracts/Command.js +2 -0
- package/dist/Contracts/CommandDefinition.d.ts +7 -0
- package/dist/Contracts/CommandDefinition.js +2 -0
- package/dist/Contracts/InputArgument.d.ts +12 -0
- package/dist/Contracts/InputArgument.js +9 -0
- package/dist/Contracts/InputOption.d.ts +15 -0
- package/dist/Contracts/InputOption.js +10 -0
- package/dist/Detector.d.ts +4 -0
- package/dist/Detector.js +73 -0
- package/dist/Executor.d.ts +4 -0
- package/dist/Executor.js +9 -0
- package/dist/Inputs/InputArgument.d.ts +13 -0
- package/dist/Inputs/InputArgument.js +39 -0
- package/dist/Inputs/InputDefinition.d.ts +11 -0
- package/dist/Inputs/InputDefinition.js +25 -0
- package/dist/Inputs/InputOption.d.ts +18 -0
- package/dist/Inputs/InputOption.js +84 -0
- package/dist/Inputs/Prompt.d.ts +5 -0
- package/dist/Inputs/Prompt.js +178 -0
- package/dist/Kernel.d.ts +15 -0
- package/dist/Kernel.js +88 -0
- package/dist/MakerCommand.d.ts +27 -0
- package/dist/MakerCommand.js +68 -0
- package/dist/Outputs/Helper.d.ts +7 -0
- package/dist/Outputs/Helper.js +61 -0
- package/dist/Outputs/OutputColor.d.ts +18 -0
- package/dist/Outputs/OutputColor.js +53 -0
- package/dist/Outputs/OutputMessage.d.ts +11 -0
- package/dist/Outputs/OutputMessage.js +70 -0
- package/dist/Outputs/ProgressBar/ProgressBar.d.ts +22 -0
- package/dist/Outputs/ProgressBar/ProgressBar.js +98 -0
- package/dist/Outputs/ProgressBar/StatusBar.d.ts +11 -0
- package/dist/Outputs/ProgressBar/StatusBar.js +10 -0
- package/dist/Outputs/ProgressBar/StatusBarFactory.d.ts +5 -0
- package/dist/Outputs/ProgressBar/StatusBarFactory.js +20 -0
- package/dist/Outputs/ProgressBar/StatusBarNormal.d.ts +6 -0
- package/dist/Outputs/ProgressBar/StatusBarNormal.js +19 -0
- package/dist/Outputs/ProgressBar/StatusBarVerbose.d.ts +6 -0
- package/dist/Outputs/ProgressBar/StatusBarVerbose.js +20 -0
- package/dist/Outputs/ProgressBar.d.ts +22 -0
- package/dist/Outputs/ProgressBar.js +123 -0
- package/dist/Parser.d.ts +9 -0
- package/dist/Parser.js +79 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +14 -0
- package/dist/src/Command.d.ts +23 -0
- package/dist/src/Command.js +61 -0
- package/dist/src/Contracts/Command.d.ts +9 -0
- package/dist/src/Contracts/Command.js +2 -0
- package/dist/src/Contracts/InputArgument.d.ts +11 -0
- package/dist/src/Contracts/InputArgument.js +9 -0
- package/dist/src/Contracts/InputOption.d.ts +12 -0
- package/dist/src/Contracts/InputOption.js +10 -0
- package/dist/src/Detector.d.ts +4 -0
- package/dist/src/Detector.js +50 -0
- package/dist/src/Inputs/InputArgument.d.ts +12 -0
- package/dist/src/Inputs/InputArgument.js +36 -0
- package/dist/src/Inputs/InputDefinition.d.ts +11 -0
- package/dist/src/Inputs/InputDefinition.js +25 -0
- package/dist/src/Inputs/InputOption.d.ts +18 -0
- package/dist/src/Inputs/InputOption.js +84 -0
- package/dist/src/Kernel.d.ts +15 -0
- package/dist/src/Kernel.js +87 -0
- package/dist/src/MakerCommand.d.ts +27 -0
- package/dist/src/MakerCommand.js +68 -0
- package/dist/src/Outputs/Helper.d.ts +7 -0
- package/dist/src/Outputs/Helper.js +61 -0
- package/dist/src/Outputs/OutputColor.d.ts +18 -0
- package/dist/src/Outputs/OutputColor.js +53 -0
- package/dist/src/Outputs/OutputMessage.d.ts +11 -0
- package/dist/src/Outputs/OutputMessage.js +144 -0
- package/dist/src/Parser.d.ts +9 -0
- package/dist/src/Parser.js +79 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +12 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, Trịnh Trần Phương Nam
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
# Noravel Command
|
|
2
|
+
|
|
3
|
+
# Installation
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @noravel/command
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
# Content
|
|
10
|
+
|
|
11
|
+
- [Create handler](#create-handler)
|
|
12
|
+
- [Create command](#create-command)
|
|
13
|
+
- [Define input expectations](#define-input-expectations)
|
|
14
|
+
- [Arguments](#arguments)
|
|
15
|
+
- [Options](#options)
|
|
16
|
+
- [Options with values](#options-with-values)
|
|
17
|
+
- [Option shortcuts](#option-shortcuts)
|
|
18
|
+
- [Input arrays](#input-arrays)
|
|
19
|
+
- [Option arrays](#option-arrays)
|
|
20
|
+
- [Input descriptions](#input-descriptions)
|
|
21
|
+
- [Command I/O](#command-io)
|
|
22
|
+
- [Create a maker command](#create-a-maker-command)
|
|
23
|
+
- [Help command](#help-command)
|
|
24
|
+
- [Programmatically executing commands](#programmatically-executing-commands)
|
|
25
|
+
|
|
26
|
+
## Create handler
|
|
27
|
+
|
|
28
|
+
Create a handler file in your project.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
touch noravel
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Open a `noravel` file and add the following code:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
#!/usr/bin/env node
|
|
38
|
+
|
|
39
|
+
import { Kernel } from '@noravel/command';
|
|
40
|
+
|
|
41
|
+
const kernel = new Kernel(process.argv);
|
|
42
|
+
kernel.run();
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Create command
|
|
46
|
+
|
|
47
|
+
Create a command file in your project.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
touch Commands/Greeting.js
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Open a `Commands/Greeting.js` file and add the following code:
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
import { Command } from '@noravel/command';
|
|
57
|
+
|
|
58
|
+
class Greeting extends Command {
|
|
59
|
+
signature = 'greeting';
|
|
60
|
+
description = 'Sending greetings to someone';
|
|
61
|
+
|
|
62
|
+
async handle() {
|
|
63
|
+
console.log('Hello World');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default Greeting;
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Make sure your command class exists the `handle` method. Then you can register your command in the `noravel` file.
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
#!/usr/bin/env node
|
|
74
|
+
|
|
75
|
+
import { Kernel } from '@noravel/command';
|
|
76
|
+
import Greeting from './Commands/Greeting.js';
|
|
77
|
+
|
|
78
|
+
const kernel = new Kernel(process.argv);
|
|
79
|
+
kernel.register([
|
|
80
|
+
Greeting,
|
|
81
|
+
]).run();
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Now, you can run your command like this:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
./noravel greeting
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Defining input expectations
|
|
91
|
+
|
|
92
|
+
When writing console commands, it is common to gather input from the user through arguments or options.
|
|
93
|
+
Noravel Command makes it very convenient to define the input you expect from the user using the `signature` property on your commands.
|
|
94
|
+
The signature property allows you to define the name, arguments, and options for the command in a single, expressive.
|
|
95
|
+
|
|
96
|
+
### Arguments
|
|
97
|
+
|
|
98
|
+
All user supplied arguments and options are wrapped in curly braces. In the following example, the command defines one required argument: `user:`
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
signature = 'greeting {user}';
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
You may also make arguments optional or define default values for arguments:
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
// Optional argument
|
|
108
|
+
signature = 'greeting {user?}';
|
|
109
|
+
|
|
110
|
+
// Optional argument with default value
|
|
111
|
+
signature = 'greeting {user=Nam}';
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Options
|
|
115
|
+
|
|
116
|
+
Options, like arguments, are another form of user input.
|
|
117
|
+
Options are prefixed by two hyphens (`--`) when they are provided via the command line.
|
|
118
|
+
There are two types of options: those that receive a value and those that don't.
|
|
119
|
+
Options that don't receive a value serve as a boolean "switch".
|
|
120
|
+
Let's take a look at an example of this type of option:
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
signature = 'greeting {--verbose}';
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
In this example, the `--verbose` switch may be specified when calling your command.
|
|
127
|
+
If the `--verbose` switch is passed, the value of the option will be `true`. Otherwise, the value will be `false`:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
./noravel greeting --verbose
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### Options with values
|
|
134
|
+
|
|
135
|
+
Next, let's take a look at an option that expects a value. If the user must specify a value for an option, you should suffix the option name with a `=` sign:
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
signature = 'greeting {--verbose=}';
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
In this example, the user may pass a value for the option like so. If the option is not specified when invoking the command, its value will be `undefined`:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
./noravel greeting --verbose=Default
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
You may assign default values to options by specifying the default value after the option name. If no option value is passed by the user, the default value will be used:
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
signature = 'greeting {--verbose=Default}';
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
#### Option shortcuts
|
|
154
|
+
|
|
155
|
+
To assign a shortcut when defining an option, you may specify it before the option name and use the `|` character as a delimiter to separate the shortcut from the full option name:
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
signature = 'greeting {--v|verbose=}';
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
When invoking the command on your terminal, option shortcuts should be prefixed with a single hyphen and no `=` character should be included when specifying a value for the option:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
./noravel greeting -vDefault
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Input arrays
|
|
168
|
+
|
|
169
|
+
If you would like to define arguments or options to expect multiple input values, you may use the `*` character.
|
|
170
|
+
First, let's take a look at an example that specifies such an argument:
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
signature = 'greeting {user*}';
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
When running this command, the user arguments may be passed in order to the command line.
|
|
177
|
+
For example, the following command will set the value of user to an array with 1 and 2 as its values:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
./noravel greeting user1 user2
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
This `*` character can be combined with an optional argument definition to allow zero or more instances of an argument:
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
signature = 'greeting {user?*}';
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Option arrays
|
|
190
|
+
|
|
191
|
+
When defining an option that expects multiple input values,
|
|
192
|
+
each option value passed to the command should be prefixed with the option name:
|
|
193
|
+
|
|
194
|
+
```js
|
|
195
|
+
signature = 'greeting {--tag=*}';
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Such a command may be invoked by passing multiple `--tag` arguments:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
./noravel greeting --tag=tag1 --tag=tag2
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Input descriptions
|
|
205
|
+
|
|
206
|
+
You may assign descriptions to input arguments and options by separating the argument name from the description using a colon.
|
|
207
|
+
If you need a little extra room to define your command, feel free to spread the definition across multiple lines:
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
signature = `greeting {name : The name of the person to greet}
|
|
211
|
+
{--v|verbose : Increase the verbosity of messages}
|
|
212
|
+
{--tag=* : Tags to apply to the greeting}`;
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Note**: If your command contains required arguments, the user will receive a thrown error when they are not provided.
|
|
216
|
+
|
|
217
|
+
## Command I/O
|
|
218
|
+
|
|
219
|
+
### Retrieving Input
|
|
220
|
+
|
|
221
|
+
While your command is executing, you will likely need to access the values for the arguments and options accepted by your command. To do so, you may use the `getArgument` and `getOption` methods. If an argument or option does not exist, `undefined` will be returned:
|
|
222
|
+
|
|
223
|
+
```js
|
|
224
|
+
async handle() {
|
|
225
|
+
const name = this.getArgument('name');
|
|
226
|
+
const verbose = this.getOption('verbose');
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
If you need to retrieve all of the arguments as an `array`, call the `getArgument` method without any parameters:
|
|
231
|
+
|
|
232
|
+
```js
|
|
233
|
+
async handle() {
|
|
234
|
+
const arguments = this.getArgument();
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Options may be retrieved just as easily as arguments using the option method. To retrieve all of the options as an array, call the `getOption` method without any parameters:
|
|
239
|
+
|
|
240
|
+
```js
|
|
241
|
+
async handle() {
|
|
242
|
+
const options = this.getOption();
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Prompting for input
|
|
247
|
+
|
|
248
|
+
In addition to displaying output, you may also ask the user to provide input during the execution of your command. The `ask` method will prompt the user with the given question, accept their input, and then return the user's input back to your command:
|
|
249
|
+
|
|
250
|
+
```js
|
|
251
|
+
async handle() {
|
|
252
|
+
const name = this.prompt.ask('What is your name?');
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The `secret` method is similar to `ask`, but the user's input will not be visible to them as they type in the console. This method is useful when asking for sensitive information such as passwords:
|
|
257
|
+
|
|
258
|
+
```js
|
|
259
|
+
async handle() {
|
|
260
|
+
const password = this.prompt.secret('What is your password?');
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Multiple choice questions**
|
|
265
|
+
|
|
266
|
+
If you need to give the user a predefined set of choices when asking a question, you may use the choice method.
|
|
267
|
+
You may set the array index of the default value to be returned if no option is chosen by passing the index as the third argument to the method:
|
|
268
|
+
|
|
269
|
+
```js
|
|
270
|
+
async handle() {
|
|
271
|
+
const choice = this.prompt.choice('What is your favorite color?', ['red', 'green', 'blue'], defaultIndex);
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
In addition, the `choice` method accepts optional fourth argument for determining whether multiple selections are permitted:
|
|
276
|
+
|
|
277
|
+
```js
|
|
278
|
+
const choices = this.prompt.choice('What is your favorite color?', ['red', 'green', 'blue'], defaultIndex, true);
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Writing Output
|
|
282
|
+
|
|
283
|
+
To send output to the console, you may use the `line`, `info`, `comment`, `success`, `warning`, and `error` methods. Each of these methods will use appropriate ANSI colors for their purpose. For example, let's display some general information to the user.
|
|
284
|
+
|
|
285
|
+
```js
|
|
286
|
+
async handle() {
|
|
287
|
+
this.output.info('This is an info message');
|
|
288
|
+
this.output.success('This is a success message');
|
|
289
|
+
this.output.warning('This is a warning message');
|
|
290
|
+
this.output.error('This is an error message');
|
|
291
|
+
this.output.comment('This is a comment message');
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**Table**
|
|
296
|
+
|
|
297
|
+
The `table` method makes it easy to correctly format multiple rows / columns of data. All you need to do is provide the column names and the data for the table and Noravel Command will automatically calculate the appropriate width and height of the table for you:
|
|
298
|
+
|
|
299
|
+
```js
|
|
300
|
+
async handle() {
|
|
301
|
+
this.output.table(
|
|
302
|
+
['id', 'name', 'email', 'date of birth'],
|
|
303
|
+
[
|
|
304
|
+
[1, 'John Doe', 'johndoe@example.com', '2000-01-01'],
|
|
305
|
+
[2, 'Jane Doe', 'janedoe@example.com', '1999-01-01'],
|
|
306
|
+
[3, 'James Doe', 'jamesdoe@example.com', '2010-01-01'],
|
|
307
|
+
[4, 'John Smith', 'johnsmith@example.com', '2000-11-01'],
|
|
308
|
+
[5, 'Jane Smith', 'janesmith@example.com', '2000-01-21'],
|
|
309
|
+
[6, 'James Smith', 'jamessmith@example.com', '2001-01-01'],
|
|
310
|
+
]
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
#### Progress Bars
|
|
316
|
+
|
|
317
|
+
For long running tasks, it can be helpful to show a progress bar that informs users how complete the task is.
|
|
318
|
+
First, define the total number of steps the process will iterate through.
|
|
319
|
+
Then, advance the progress bar after processing each item.
|
|
320
|
+
|
|
321
|
+
```js
|
|
322
|
+
async handle() {
|
|
323
|
+
const bar = this.output.createProgressBar(100);
|
|
324
|
+
bar.start();
|
|
325
|
+
for (let i = 0; i < units; i++) {
|
|
326
|
+
// Do some work...
|
|
327
|
+
bar.advance();
|
|
328
|
+
}
|
|
329
|
+
bar.finish();
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Progress Bar Methods**
|
|
334
|
+
|
|
335
|
+
The progress bar provides several methods to control its behavior:
|
|
336
|
+
|
|
337
|
+
```js
|
|
338
|
+
async handle() {
|
|
339
|
+
const bar = this.output.createProgressBar(100);
|
|
340
|
+
|
|
341
|
+
// Start the progress bar with an optional message
|
|
342
|
+
bar.start('Processing items...');
|
|
343
|
+
|
|
344
|
+
// Advance the progress by one step
|
|
345
|
+
bar.advance();
|
|
346
|
+
|
|
347
|
+
// Set a different total after creation
|
|
348
|
+
bar.setTotal(150);
|
|
349
|
+
|
|
350
|
+
// Get the current total
|
|
351
|
+
const total = bar.getTotal();
|
|
352
|
+
|
|
353
|
+
// Set the progress bar size (in characters)
|
|
354
|
+
bar.setSize(50);
|
|
355
|
+
|
|
356
|
+
// Get the current size
|
|
357
|
+
const size = bar.getSize();
|
|
358
|
+
|
|
359
|
+
// Finish the progress bar with an optional message
|
|
360
|
+
bar.finish('Processing completed!');
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
**Progress Bar Formats**
|
|
365
|
+
|
|
366
|
+
The progress bar supports three different display formats:
|
|
367
|
+
|
|
368
|
+
```js
|
|
369
|
+
async handle() {
|
|
370
|
+
const bar = this.output.createProgressBar(100);
|
|
371
|
+
|
|
372
|
+
// Normal format (default): [=====> ] 50.0% 50/100
|
|
373
|
+
bar.setFormat('normal');
|
|
374
|
+
|
|
375
|
+
// Verbose format: [=====>] 50.0% 50/100 remaining: 25.0s. elapsed: 12.5s.
|
|
376
|
+
bar.setFormat('verbose');
|
|
377
|
+
|
|
378
|
+
// Minimal format: Progress: 50.0%
|
|
379
|
+
bar.setFormat('minimal');
|
|
380
|
+
|
|
381
|
+
bar.start();
|
|
382
|
+
for (let i = 0; i < 100; i++) {
|
|
383
|
+
bar.advance();
|
|
384
|
+
}
|
|
385
|
+
bar.finish();
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
**Progress Bar Styles**
|
|
390
|
+
|
|
391
|
+
You can choose between different progress bar styles:
|
|
392
|
+
|
|
393
|
+
```js
|
|
394
|
+
async handle() {
|
|
395
|
+
const bar = this.output.createProgressBar(100);
|
|
396
|
+
|
|
397
|
+
// Arrow style (default): [=====>-----]
|
|
398
|
+
bar.useArrowProgress();
|
|
399
|
+
|
|
400
|
+
// Shape style: [#####_____]
|
|
401
|
+
bar.useShapeProgress();
|
|
402
|
+
|
|
403
|
+
// Block style: [█████░░░░░]
|
|
404
|
+
bar.useBlockProgress();
|
|
405
|
+
|
|
406
|
+
bar.start();
|
|
407
|
+
for (let i = 0; i < 100; i++) {
|
|
408
|
+
bar.advance();
|
|
409
|
+
}
|
|
410
|
+
bar.finish();
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
**Constructor Options**
|
|
415
|
+
|
|
416
|
+
When creating a progress bar, you can specify custom options:
|
|
417
|
+
|
|
418
|
+
```js
|
|
419
|
+
async handle() {
|
|
420
|
+
// Create progress bar with custom size (in characters)
|
|
421
|
+
const bar = this.output.createProgressBar(100, 80);
|
|
422
|
+
|
|
423
|
+
// Create progress bar that uses full terminal width
|
|
424
|
+
const fullBar = this.output.createProgressBar(100, null);
|
|
425
|
+
|
|
426
|
+
bar.start();
|
|
427
|
+
for (let i = 0; i < 100; i++) {
|
|
428
|
+
bar.advance();
|
|
429
|
+
}
|
|
430
|
+
bar.finish();
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
## Create a maker command
|
|
437
|
+
|
|
438
|
+
Create a command file in your project.
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
touch Commands/ControllerMaker.js
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
Open a `Commands/ControllerMaker.js` file and add the following code:
|
|
445
|
+
|
|
446
|
+
```js
|
|
447
|
+
import { MakerCommand } from '@noravel/command';
|
|
448
|
+
|
|
449
|
+
class ControllerMaker extends MakerCommand {
|
|
450
|
+
signature = `make:controller
|
|
451
|
+
{name : The name of the controller}
|
|
452
|
+
{--m|model= : Generate a resource controller for the given model}
|
|
453
|
+
{--f|force : Create the class even if the controller already exists}`;
|
|
454
|
+
description = 'Create a new controller class';
|
|
455
|
+
|
|
456
|
+
async handle() {
|
|
457
|
+
console.log('Creating controller...');
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export default ControllerMaker;
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
Now let register the command in the `noravel` file:
|
|
465
|
+
|
|
466
|
+
```js
|
|
467
|
+
#!/usr/bin/env node
|
|
468
|
+
|
|
469
|
+
import { Kernel } from '@noravel/command';
|
|
470
|
+
import Greeting from './Commands/Greeting.js';
|
|
471
|
+
import ControllerMaker from './Commands/ControllerMaker.js';
|
|
472
|
+
|
|
473
|
+
const kernel = new Kernel(process.argv);
|
|
474
|
+
kernel.register([
|
|
475
|
+
Greeting,
|
|
476
|
+
ControllerMaker,
|
|
477
|
+
]).run();
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
Let's look at the methods available in the `MakerCommand` class:
|
|
481
|
+
|
|
482
|
+
```ts
|
|
483
|
+
class MakerCommand {
|
|
484
|
+
// Returns the base path of the project
|
|
485
|
+
public basePath(dirPath?: string): string;
|
|
486
|
+
|
|
487
|
+
// Returns the filename; if the user hasn't entered it, it will wait for user input
|
|
488
|
+
public async fileName(question?: string = 'What should the file be named?'): Promise<string>;
|
|
489
|
+
|
|
490
|
+
// Creates a file with the given name and path
|
|
491
|
+
public makeFile(fileName: string, dirPath: string): void;
|
|
492
|
+
|
|
493
|
+
// Returns the message that the file already exists
|
|
494
|
+
protected fileAlreadyExists(filePath: string): string;
|
|
495
|
+
|
|
496
|
+
// Returns the message that the file created successfully
|
|
497
|
+
protected fileCreatedSuccessfully(filePath: string): string;
|
|
498
|
+
|
|
499
|
+
// Returns the contents of a file that will be written to the file being created
|
|
500
|
+
protected contentFile(fileName: string): string;
|
|
501
|
+
}
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
## Help command
|
|
505
|
+
|
|
506
|
+
Check the available commands:
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
./noravel --help
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
Output:
|
|
513
|
+
|
|
514
|
+
```
|
|
515
|
+
Usage:
|
|
516
|
+
command [arguments] [--options]
|
|
517
|
+
|
|
518
|
+
Available commands:
|
|
519
|
+
greeting Sending greetings to someone
|
|
520
|
+
make:controller Create a new controller class
|
|
521
|
+
|
|
522
|
+
Options:
|
|
523
|
+
-h, --help Show this help message
|
|
524
|
+
-v, --version Display the version of Noravel Command
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
You can also view a description of a specific command by:
|
|
528
|
+
|
|
529
|
+
```bash
|
|
530
|
+
./noravel make:controller --help
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
Output:
|
|
534
|
+
|
|
535
|
+
```
|
|
536
|
+
Description:
|
|
537
|
+
Create a new controller class
|
|
538
|
+
|
|
539
|
+
Usage:
|
|
540
|
+
make:controller [options] [--] <name>
|
|
541
|
+
|
|
542
|
+
Arguments:
|
|
543
|
+
name The name of the controller
|
|
544
|
+
|
|
545
|
+
Options:
|
|
546
|
+
-m, --model Generate a resource controller for the given model
|
|
547
|
+
-f, --force Create the class even if the controller already exists
|
|
548
|
+
-h, --help Display help for the given command
|
|
549
|
+
-v, --version Display the version of Noravel Command
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
## Programmatically executing commands
|
|
553
|
+
|
|
554
|
+
Sometimes you may wish to execute your command outside of the CLI.
|
|
555
|
+
For example, you may wish to execute your command from a route or controller.
|
|
556
|
+
You may use the `call` method on the `Executor` to accomplish this.
|
|
557
|
+
The `call` method accepts either the command's class name as its first argument, and an array of command parameters as the second argument.
|
|
558
|
+
|
|
559
|
+
```js
|
|
560
|
+
import Executor from './Executor.js';
|
|
561
|
+
import Greeting from './Commands/Greeting.js';
|
|
562
|
+
|
|
563
|
+
await Executor.call(Greeting, [
|
|
564
|
+
'John Doe',
|
|
565
|
+
'--verbose',
|
|
566
|
+
'--tag=foo',
|
|
567
|
+
'--tag=bar',
|
|
568
|
+
]);
|
|
569
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import CommandContract from './Contracts/Command';
|
|
2
|
+
import InputDefinition from './Inputs/InputDefinition';
|
|
3
|
+
import Prompt from './Inputs/Prompt';
|
|
4
|
+
import OutputMessage from './Outputs/OutputMessage';
|
|
5
|
+
export default abstract class Command implements CommandContract {
|
|
6
|
+
argv: string[];
|
|
7
|
+
protected signature: string;
|
|
8
|
+
protected name: string;
|
|
9
|
+
protected description: string;
|
|
10
|
+
protected definition: InputDefinition;
|
|
11
|
+
protected inputs: {
|
|
12
|
+
arguments: Record<string, unknown>;
|
|
13
|
+
options: Record<string, unknown>;
|
|
14
|
+
};
|
|
15
|
+
protected output: OutputMessage;
|
|
16
|
+
protected prompt: Prompt;
|
|
17
|
+
constructor(argv: string[]);
|
|
18
|
+
bootstrap(): this;
|
|
19
|
+
detect(): this;
|
|
20
|
+
getName(): string;
|
|
21
|
+
getDescription(): string;
|
|
22
|
+
getArgument(name?: string): unknown;
|
|
23
|
+
getOption(name?: string): unknown;
|
|
24
|
+
showHelp(): void;
|
|
25
|
+
exec(command: string): Promise<unknown>;
|
|
26
|
+
abstract handle(): void;
|
|
27
|
+
}
|
package/dist/Command.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
|
+
const Parser_1 = __importDefault(require("./Parser"));
|
|
8
|
+
const Detector_1 = __importDefault(require("./Detector"));
|
|
9
|
+
const InputDefinition_1 = __importDefault(require("./Inputs/InputDefinition"));
|
|
10
|
+
const Prompt_1 = __importDefault(require("./Inputs/Prompt"));
|
|
11
|
+
const OutputMessage_1 = __importDefault(require("./Outputs/OutputMessage"));
|
|
12
|
+
const Helper_1 = __importDefault(require("./Outputs/Helper"));
|
|
13
|
+
class Command {
|
|
14
|
+
argv;
|
|
15
|
+
signature = '';
|
|
16
|
+
name = '';
|
|
17
|
+
description = '';
|
|
18
|
+
definition;
|
|
19
|
+
inputs = {
|
|
20
|
+
arguments: {},
|
|
21
|
+
options: {},
|
|
22
|
+
};
|
|
23
|
+
output;
|
|
24
|
+
prompt;
|
|
25
|
+
constructor(argv) {
|
|
26
|
+
this.argv = argv;
|
|
27
|
+
this.definition = new InputDefinition_1.default();
|
|
28
|
+
this.output = new OutputMessage_1.default();
|
|
29
|
+
this.prompt = new Prompt_1.default();
|
|
30
|
+
}
|
|
31
|
+
bootstrap() {
|
|
32
|
+
this.signature = this.signature + ' {--h|help : Display help for the given command} {--v|version : Display the version of Noravel Command}';
|
|
33
|
+
const [name, argumentDefinition, optionDefinition] = Parser_1.default.parse(this.signature);
|
|
34
|
+
this.name = name;
|
|
35
|
+
this.definition.setArguments(argumentDefinition);
|
|
36
|
+
this.definition.setOptions(optionDefinition);
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
detect() {
|
|
40
|
+
const [_arguments, _options] = Detector_1.default.detect(this.argv.slice(1), this.definition);
|
|
41
|
+
this.inputs.arguments = _arguments;
|
|
42
|
+
this.inputs.options = _options;
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
getName() {
|
|
46
|
+
return this.name;
|
|
47
|
+
}
|
|
48
|
+
getDescription() {
|
|
49
|
+
return this.description;
|
|
50
|
+
}
|
|
51
|
+
getArgument(name) {
|
|
52
|
+
if (!name) {
|
|
53
|
+
return Object.values(this.inputs.arguments);
|
|
54
|
+
}
|
|
55
|
+
return this.inputs.arguments[name];
|
|
56
|
+
}
|
|
57
|
+
getOption(name) {
|
|
58
|
+
if (!name) {
|
|
59
|
+
return Object.values(this.inputs.options);
|
|
60
|
+
}
|
|
61
|
+
return this.inputs.options[name];
|
|
62
|
+
}
|
|
63
|
+
showHelp() {
|
|
64
|
+
const helper = new Helper_1.default();
|
|
65
|
+
helper.showHelp(this.name, this.description, this.definition);
|
|
66
|
+
}
|
|
67
|
+
async exec(command) {
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
(0, node_child_process_1.exec)(command, (error, stdout, stderr) => {
|
|
70
|
+
if (error) {
|
|
71
|
+
this.output.error(error.message);
|
|
72
|
+
reject(error);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (stderr) {
|
|
76
|
+
this.output.error(stderr);
|
|
77
|
+
reject(new Error(stderr));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
this.output.info(stdout);
|
|
81
|
+
resolve(stdout);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.default = Command;
|