@tkeron/commands 0.3.1 → 0.4.1
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/.github/workflows/npm_deploy.yml +13 -7
- package/bun.lockb +0 -0
- package/changelog.md +21 -0
- package/examples/example.ts +116 -0
- package/package.json +8 -2
- package/readme.md +119 -2
- package/src/applyDefaults.ts +20 -0
- package/src/getCommandsFuncs.ts +92 -21
- package/src/getStart.ts +23 -28
- package/src/index.ts +1 -1
- package/src/parseArgs.ts +233 -0
- package/src/textFuncs.ts +32 -7
- package/src/types.ts +59 -4
- package/src/validateOptions.ts +39 -0
- package/tests/applyDefaults.test.ts +133 -0
- package/tests/backward-compatibility.test.ts +187 -0
- package/tests/example.e2e.test.ts +42 -0
- package/tests/getCommandsFuncs.test.ts +492 -0
- package/tests/getStart.test.ts +265 -0
- package/{src → tests/helpers}/testConstants.ts +1 -1
- package/tests/parseArgs.test.ts +565 -0
- package/tests/textFuncs.test.ts +179 -0
- package/tests/validateOptions.test.ts +157 -0
- package/tests/version-command.test.ts +292 -0
- package/tsconfig.json +2 -3
- package/src/example.e2e.test.ts +0 -37
- package/src/example.ts +0 -69
- package/src/getCommandsFuncs.test.ts +0 -94
- package/src/getStart.test.ts +0 -79
- package/src/textFuncs.test.ts +0 -59
|
@@ -4,21 +4,27 @@ on:
|
|
|
4
4
|
branches:
|
|
5
5
|
- main
|
|
6
6
|
|
|
7
|
+
permissions:
|
|
8
|
+
id-token: write
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
7
11
|
jobs:
|
|
8
12
|
build-test-publish:
|
|
9
13
|
runs-on: ubuntu-latest
|
|
14
|
+
|
|
10
15
|
steps:
|
|
11
16
|
- uses: actions/checkout@v4
|
|
12
|
-
|
|
17
|
+
|
|
18
|
+
- uses: actions/setup-node@v4
|
|
13
19
|
with:
|
|
14
|
-
node-version:
|
|
15
|
-
registry-url: "https://registry.npmjs.org
|
|
20
|
+
node-version: 22.x
|
|
21
|
+
registry-url: "https://registry.npmjs.org"
|
|
16
22
|
|
|
17
23
|
- uses: oven-sh/setup-bun@v2
|
|
18
24
|
|
|
19
|
-
- run:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
- run: bun i
|
|
26
|
+
- run: bun test
|
|
27
|
+
|
|
28
|
+
- run: npm publish --provenance
|
|
23
29
|
env:
|
|
24
30
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/bun.lockb
CHANGED
|
Binary file
|
package/changelog.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
# v0.4.1
|
|
2
|
+
|
|
3
|
+
- add comprehensive test coverage for version command
|
|
4
|
+
- add publishConfig to package.json for npm publishing
|
|
5
|
+
|
|
6
|
+
# v0.4.0
|
|
7
|
+
|
|
8
|
+
- add standard CLI syntax support (`--flag`, `-f`, `--opt value`, `--opt=value`)
|
|
9
|
+
- add typed option definitions (`OptionDefinition`) with `boolean`, `string`, `number` types
|
|
10
|
+
- add `addFlag()`, `addStringOption()`, `addNumberOption()` builder methods
|
|
11
|
+
- add argument parser (`parseArgs`) with auto-detection of legacy/standard/mixed syntax
|
|
12
|
+
- add default values via `applyDefaults()`
|
|
13
|
+
- add option validation (`validateOptions`) with required, type, and allowed values checks
|
|
14
|
+
- add combined short flags support (`-vw`)
|
|
15
|
+
- add negated flags (`--no-verbose`)
|
|
16
|
+
- add double-dash separator (`--`) to stop parsing
|
|
17
|
+
- add option aliases support
|
|
18
|
+
- maintain full backward compatibility with legacy `key=value` syntax
|
|
19
|
+
- refactor `getStart` to use new parsing pipeline
|
|
20
|
+
- update help text generation for typed options
|
|
21
|
+
|
|
1
22
|
# v0.3.0
|
|
2
23
|
|
|
3
24
|
- migrate project to bun
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { getCommands } from "../src/index.js";
|
|
2
|
+
import type { Commands } from "../src/types.js";
|
|
3
|
+
|
|
4
|
+
const commands = getCommands("test program", "0.0.14")
|
|
5
|
+
.addCommand("com1")
|
|
6
|
+
.addAlias("c1")
|
|
7
|
+
.addOption("opt1")
|
|
8
|
+
.addOption("opt2")
|
|
9
|
+
.addDescription("command 001 test...")
|
|
10
|
+
.addPositionedArgument("pos0")
|
|
11
|
+
.addPositionedArgument("pos1")
|
|
12
|
+
.setCallback(console.log)
|
|
13
|
+
|
|
14
|
+
.commands()
|
|
15
|
+
|
|
16
|
+
.addCommand("com2")
|
|
17
|
+
.addAlias("c2")
|
|
18
|
+
.addAlias("a2")
|
|
19
|
+
.addOption("opt1")
|
|
20
|
+
.addDescription("command 002 test...")
|
|
21
|
+
.setCallback(console.log)
|
|
22
|
+
|
|
23
|
+
.commands()
|
|
24
|
+
|
|
25
|
+
.addCommand("com3")
|
|
26
|
+
.addAlias("c3")
|
|
27
|
+
.addOption("opt1")
|
|
28
|
+
.addOption("opt2", "exOpt2...")
|
|
29
|
+
.addOption("opt3")
|
|
30
|
+
.addDescription("command 003 test...")
|
|
31
|
+
.addPositionedArgument("pos0")
|
|
32
|
+
.addPositionedArgument("pos1")
|
|
33
|
+
.addPositionedArgument("pos3")
|
|
34
|
+
.setCallback(console.log)
|
|
35
|
+
|
|
36
|
+
.commands()
|
|
37
|
+
|
|
38
|
+
.addHeaderText("header...\n\n")
|
|
39
|
+
.addFooterText("\n\nFooter...");
|
|
40
|
+
|
|
41
|
+
commands.start([
|
|
42
|
+
"",
|
|
43
|
+
"",
|
|
44
|
+
..."com1 pos0value opt1=qw111erty pos1value opt2=as222d asdasd"
|
|
45
|
+
.replace(/\s+/g, " ")
|
|
46
|
+
.split(" "),
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
commands.start([
|
|
50
|
+
"",
|
|
51
|
+
"",
|
|
52
|
+
..."a2 pos0value opt1=qw111erty pos1value opt2=as222d asdasd"
|
|
53
|
+
.replace(/\s+/g, " ")
|
|
54
|
+
.split(" "),
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
commands.start([
|
|
58
|
+
"",
|
|
59
|
+
"",
|
|
60
|
+
..."c3 pos0value opt1=qw111erty pos1value opt2=as222d asdasd"
|
|
61
|
+
.replace(/\s+/g, " ")
|
|
62
|
+
.split(" "),
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
const standardCommands = getCommands("myapp", "1.0.0")
|
|
66
|
+
.addCommand("build")
|
|
67
|
+
.addAlias("b")
|
|
68
|
+
.addDescription("Build project")
|
|
69
|
+
.addFlag("verbose", "v", "Verbose output")
|
|
70
|
+
.addFlag("watch", "w", "Watch mode")
|
|
71
|
+
.addStringOption("output", "o", "Output directory", { default: "./dist" })
|
|
72
|
+
.addPositionedArgument("source")
|
|
73
|
+
.setCallback(console.log)
|
|
74
|
+
|
|
75
|
+
.commands()
|
|
76
|
+
|
|
77
|
+
.addCommand("serve")
|
|
78
|
+
.addAlias("s")
|
|
79
|
+
.addDescription("Start dev server")
|
|
80
|
+
.addNumberOption("port", "p", "Port number", { default: 3000 })
|
|
81
|
+
.addStringOption("host", "h", "Host", { default: "localhost" })
|
|
82
|
+
.addFlag("open", "o", "Open browser")
|
|
83
|
+
.setCallback(console.log)
|
|
84
|
+
|
|
85
|
+
.commands()
|
|
86
|
+
|
|
87
|
+
.addCommand("test")
|
|
88
|
+
.addAlias("t")
|
|
89
|
+
.addDescription("Run tests")
|
|
90
|
+
.addStringOption("filter", "f", "Test filter")
|
|
91
|
+
.addFlag("coverage", "c", "Coverage report")
|
|
92
|
+
.addNumberOption("timeout", undefined, "Timeout in ms", { default: 5000 })
|
|
93
|
+
.setCallback(console.log)
|
|
94
|
+
|
|
95
|
+
.commands()
|
|
96
|
+
|
|
97
|
+
.addHeaderText("MyApp CLI v1.0.0\n")
|
|
98
|
+
.addFooterText("\nFor more info: https://example.com");
|
|
99
|
+
|
|
100
|
+
standardCommands.start(["", "", "build", "src", "--verbose", "-o", "build"]);
|
|
101
|
+
|
|
102
|
+
standardCommands.start(["", "", "serve", "-p", "8080", "--open"]);
|
|
103
|
+
|
|
104
|
+
standardCommands.start(["", "", "test", "--filter", "unit", "--coverage"]);
|
|
105
|
+
|
|
106
|
+
standardCommands.start(["", "", "b", "-vw"]);
|
|
107
|
+
|
|
108
|
+
standardCommands.start(["", "", "build", "src", "--verbose", "extra=legacy"]);
|
|
109
|
+
|
|
110
|
+
declare global {
|
|
111
|
+
var commands: Commands;
|
|
112
|
+
var standardCommands: Commands;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
globalThis.commands = commands;
|
|
116
|
+
globalThis.standardCommands = standardCommands;
|
package/package.json
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tkeron/commands",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "library for handling command line arguments",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"module": "src/index.ts",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"author": "tkeron",
|
|
9
9
|
"license": "MIT",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "bun test --concurrent"
|
|
12
|
+
},
|
|
10
13
|
"devDependencies": {
|
|
11
|
-
"@types/bun": "^1.3.
|
|
14
|
+
"@types/bun": "^1.3.9"
|
|
12
15
|
},
|
|
13
16
|
"peerDependencies": {
|
|
14
17
|
"typescript": "^5.9.3"
|
|
@@ -19,6 +22,9 @@
|
|
|
19
22
|
"command-line",
|
|
20
23
|
"arguments"
|
|
21
24
|
],
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
22
28
|
"repository": {
|
|
23
29
|
"url": "git@github.com:tkeron/commands.git"
|
|
24
30
|
}
|
package/readme.md
CHANGED
|
@@ -1,3 +1,120 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @tkeron/commands
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Zero-dependency CLI command parser and router for Bun/Node.js.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @tkeron/commands
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { getCommands } from "@tkeron/commands";
|
|
15
|
+
|
|
16
|
+
const cli = getCommands("myapp", "1.0.0")
|
|
17
|
+
.addCommand("build")
|
|
18
|
+
.addAlias("b")
|
|
19
|
+
.addDescription("Build the project")
|
|
20
|
+
.addFlag("verbose", "v", "Enable verbose output")
|
|
21
|
+
.addStringOption("output", "o", "Output directory", { default: "./dist" })
|
|
22
|
+
.addPositionedArgument("source")
|
|
23
|
+
.setCallback(console.log)
|
|
24
|
+
|
|
25
|
+
.commands()
|
|
26
|
+
|
|
27
|
+
.addCommand("serve")
|
|
28
|
+
.addAlias("s")
|
|
29
|
+
.addDescription("Start dev server")
|
|
30
|
+
.addNumberOption("port", "p", "Port number", { default: 3000 })
|
|
31
|
+
.addFlag("open", "o", "Open browser")
|
|
32
|
+
.setCallback(console.log)
|
|
33
|
+
|
|
34
|
+
.commands()
|
|
35
|
+
|
|
36
|
+
.addHeaderText("MyApp v1.0.0\n")
|
|
37
|
+
.addFooterText("\nDocs: https://example.com");
|
|
38
|
+
|
|
39
|
+
cli.start();
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Usage:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
myapp build src --verbose -o dist
|
|
46
|
+
myapp serve -p 8080 --open
|
|
47
|
+
myapp help
|
|
48
|
+
myapp version
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## API
|
|
52
|
+
|
|
53
|
+
### `getCommands(programName, version)`
|
|
54
|
+
|
|
55
|
+
Creates a new `Commands` instance.
|
|
56
|
+
|
|
57
|
+
### Commands
|
|
58
|
+
|
|
59
|
+
| Method | Description |
|
|
60
|
+
| ---------------------- | ------------------------------------------------- |
|
|
61
|
+
| `.addCommand(name)` | Add a command, returns `CommandFactory` |
|
|
62
|
+
| `.addHeaderText(text)` | Set help header text |
|
|
63
|
+
| `.addFooterText(text)` | Set help footer text |
|
|
64
|
+
| `.start(argv?)` | Parse and execute. Uses `process.argv` by default |
|
|
65
|
+
|
|
66
|
+
### CommandFactory (chaining)
|
|
67
|
+
|
|
68
|
+
| Method | Description |
|
|
69
|
+
| ----------------------------------------------------------- | ----------------------------------------- |
|
|
70
|
+
| `.addAlias(alias)` | Add command alias |
|
|
71
|
+
| `.addDescription(text)` | Set command description |
|
|
72
|
+
| `.addFlag(name, shortFlag?, description?)` | Add boolean flag (`--verbose`, `-v`) |
|
|
73
|
+
| `.addStringOption(name, shortFlag?, description?, config?)` | Add string option |
|
|
74
|
+
| `.addNumberOption(name, shortFlag?, description?, config?)` | Add number option |
|
|
75
|
+
| `.addOption(name, example?)` | Add legacy `key=value` option |
|
|
76
|
+
| `.addPositionedArgument(name)` | Add positional argument |
|
|
77
|
+
| `.setCallback(fn)` | Set command handler |
|
|
78
|
+
| `.commands()` | Return to `Commands` to add more commands |
|
|
79
|
+
|
|
80
|
+
### Option Config
|
|
81
|
+
|
|
82
|
+
`addStringOption` and `addNumberOption` accept an optional config object:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
{
|
|
86
|
+
default?: string | number | boolean;
|
|
87
|
+
required?: boolean;
|
|
88
|
+
aliases?: string[];
|
|
89
|
+
allowedValues?: (string | number)[];
|
|
90
|
+
valueName?: string;
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Syntax Support
|
|
95
|
+
|
|
96
|
+
Both syntaxes work and can be mixed:
|
|
97
|
+
|
|
98
|
+
**Standard** (new):
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
myapp build --verbose --output dist
|
|
102
|
+
myapp build -v -o dist
|
|
103
|
+
myapp build -vw # combined short flags
|
|
104
|
+
myapp build --no-verbose # negated flags
|
|
105
|
+
myapp build -- --not-parsed # stop parsing after --
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Legacy** (backward compatible):
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
myapp build output=dist format=json
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Built-in Commands
|
|
115
|
+
|
|
116
|
+
`help` and `version` are added automatically.
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Command, ParsedOptions } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export const applyDefaults = (
|
|
4
|
+
parsed: ParsedOptions,
|
|
5
|
+
command: Command,
|
|
6
|
+
): ParsedOptions => {
|
|
7
|
+
const result = { ...parsed };
|
|
8
|
+
|
|
9
|
+
if (!command.optionDefinitions || command.optionDefinitions.length === 0) {
|
|
10
|
+
return result;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
for (const def of command.optionDefinitions) {
|
|
14
|
+
if (def.default !== undefined && result[def.name] === undefined) {
|
|
15
|
+
result[def.name] = def.default;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return result;
|
|
20
|
+
};
|
package/src/getCommandsFuncs.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { getStart } from "./getStart";
|
|
2
|
-
import { buildHelpText, getCommandText } from "./textFuncs";
|
|
1
|
+
import { getStart } from "./getStart.js";
|
|
2
|
+
import { buildHelpText, getCommandText } from "./textFuncs.js";
|
|
3
3
|
import type {
|
|
4
4
|
CommandFactory,
|
|
5
5
|
Command,
|
|
6
6
|
Commands,
|
|
7
7
|
CommandsCollection,
|
|
8
8
|
Callback,
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
OptionDefinition,
|
|
10
|
+
} from "./types.js";
|
|
11
|
+
export * from "./types.js";
|
|
11
12
|
|
|
12
13
|
export const getCommands = (
|
|
13
14
|
programName: string = "program",
|
|
14
|
-
version: string = "0.0.1"
|
|
15
|
+
version: string = "0.0.1",
|
|
15
16
|
): Commands => {
|
|
16
17
|
const commandsCollection: CommandsCollection = {};
|
|
17
18
|
|
|
@@ -41,10 +42,13 @@ export const initCommands = (
|
|
|
41
42
|
name: undefined,
|
|
42
43
|
addAlias: undefined,
|
|
43
44
|
addOption: undefined,
|
|
45
|
+
addFlag: undefined,
|
|
46
|
+
addStringOption: undefined,
|
|
47
|
+
addNumberOption: undefined,
|
|
44
48
|
addPositionedArgument: undefined,
|
|
45
49
|
addDescription: undefined,
|
|
46
50
|
setCallback: undefined,
|
|
47
|
-
})
|
|
51
|
+
}),
|
|
48
52
|
): Commands => {
|
|
49
53
|
if (!commandFactory.commands) commandFactory.commands = () => commands;
|
|
50
54
|
commands.addCommand = getAddCommand(commandsCollection, commandFactory);
|
|
@@ -57,15 +61,15 @@ export const initCommands = (
|
|
|
57
61
|
|
|
58
62
|
export const initHelpAndVersion = (
|
|
59
63
|
commands: Commands,
|
|
60
|
-
commandsCollection: CommandsCollection
|
|
64
|
+
commandsCollection: CommandsCollection,
|
|
61
65
|
) => {
|
|
62
66
|
const helpCallback = () =>
|
|
63
|
-
console
|
|
67
|
+
console.log(buildHelpText(commands, commandsCollection));
|
|
64
68
|
const versionCallback = () =>
|
|
65
|
-
console
|
|
69
|
+
console.log(
|
|
66
70
|
`${commands.headerText || ""}\n${commands.version}\n${
|
|
67
71
|
commands.footerText || ""
|
|
68
|
-
}\n
|
|
72
|
+
}\n`,
|
|
69
73
|
);
|
|
70
74
|
|
|
71
75
|
commands
|
|
@@ -96,6 +100,7 @@ export const getAddCommand =
|
|
|
96
100
|
options: [],
|
|
97
101
|
optionsExamples: [],
|
|
98
102
|
positionedArguments: [],
|
|
103
|
+
optionDefinitions: [],
|
|
99
104
|
getHelpLine: undefined,
|
|
100
105
|
});
|
|
101
106
|
commandsCollection[commandName] = command;
|
|
@@ -103,17 +108,26 @@ export const getAddCommand =
|
|
|
103
108
|
|
|
104
109
|
commandFactory.addAlias = getAddAlias(commandFactory, commandsCollection);
|
|
105
110
|
commandFactory.addOption = getAddOption(commandFactory, commandsCollection);
|
|
111
|
+
commandFactory.addFlag = getAddFlag(commandFactory, commandsCollection);
|
|
112
|
+
commandFactory.addStringOption = getAddStringOption(
|
|
113
|
+
commandFactory,
|
|
114
|
+
commandsCollection,
|
|
115
|
+
);
|
|
116
|
+
commandFactory.addNumberOption = getAddNumberOption(
|
|
117
|
+
commandFactory,
|
|
118
|
+
commandsCollection,
|
|
119
|
+
);
|
|
106
120
|
commandFactory.addDescription = getAddDescription(
|
|
107
121
|
commandFactory,
|
|
108
|
-
commandsCollection
|
|
122
|
+
commandsCollection,
|
|
109
123
|
);
|
|
110
124
|
commandFactory.setCallback = getSetCallback(
|
|
111
125
|
commandFactory,
|
|
112
|
-
commandsCollection
|
|
126
|
+
commandsCollection,
|
|
113
127
|
);
|
|
114
128
|
commandFactory.addPositionedArgument = getAddPositionedArgument(
|
|
115
129
|
commandFactory,
|
|
116
|
-
commandsCollection
|
|
130
|
+
commandsCollection,
|
|
117
131
|
);
|
|
118
132
|
|
|
119
133
|
return commandFactory;
|
|
@@ -121,7 +135,7 @@ export const getAddCommand =
|
|
|
121
135
|
|
|
122
136
|
export const getAddAlias = (
|
|
123
137
|
commandFactory: CommandFactory,
|
|
124
|
-
commandsCollection: CommandsCollection
|
|
138
|
+
commandsCollection: CommandsCollection,
|
|
125
139
|
) => {
|
|
126
140
|
return (alias: string) => {
|
|
127
141
|
commandsCollection[commandFactory.name].aliases.push(alias);
|
|
@@ -133,22 +147,28 @@ export const getAddAlias = (
|
|
|
133
147
|
|
|
134
148
|
export const getAddOption =
|
|
135
149
|
(commandFactory: CommandFactory, commandsCollection: CommandsCollection) =>
|
|
136
|
-
(option: string, example?: string) => {
|
|
137
|
-
|
|
138
|
-
|
|
150
|
+
(option: string | OptionDefinition, example?: string) => {
|
|
151
|
+
if (typeof option === "string") {
|
|
152
|
+
commandsCollection[commandFactory.name].options.push(option);
|
|
153
|
+
commandsCollection[commandFactory.name].optionsExamples.push(
|
|
154
|
+
example || "",
|
|
155
|
+
);
|
|
156
|
+
} else {
|
|
157
|
+
commandsCollection[commandFactory.name].optionDefinitions.push(option);
|
|
158
|
+
}
|
|
139
159
|
|
|
140
160
|
return commandFactory;
|
|
141
161
|
};
|
|
142
162
|
|
|
143
163
|
export const getAddPositionedArgument = (
|
|
144
164
|
commandFactory: CommandFactory,
|
|
145
|
-
commandsCollection: CommandsCollection
|
|
165
|
+
commandsCollection: CommandsCollection,
|
|
146
166
|
) => {
|
|
147
167
|
return (arg: string): CommandFactory => {
|
|
148
168
|
const positionedArguments =
|
|
149
169
|
commandsCollection[commandFactory.name].positionedArguments;
|
|
150
170
|
|
|
151
|
-
if (!positionedArguments.includes(
|
|
171
|
+
if (!positionedArguments.includes(arg)) {
|
|
152
172
|
positionedArguments.push(arg);
|
|
153
173
|
}
|
|
154
174
|
|
|
@@ -158,7 +178,7 @@ export const getAddPositionedArgument = (
|
|
|
158
178
|
|
|
159
179
|
export const getSetCallback = (
|
|
160
180
|
commandFactory: CommandFactory,
|
|
161
|
-
commandsCollection: CommandsCollection
|
|
181
|
+
commandsCollection: CommandsCollection,
|
|
162
182
|
) => {
|
|
163
183
|
return (fn: Callback): CommandFactory => {
|
|
164
184
|
commandsCollection[commandFactory.name].callback = fn;
|
|
@@ -169,7 +189,7 @@ export const getSetCallback = (
|
|
|
169
189
|
|
|
170
190
|
export const getAddDescription = (
|
|
171
191
|
commandFactory: CommandFactory,
|
|
172
|
-
commandsCollection: CommandsCollection
|
|
192
|
+
commandsCollection: CommandsCollection,
|
|
173
193
|
) => {
|
|
174
194
|
return (description: string) => {
|
|
175
195
|
commandsCollection[commandFactory.name].description = description;
|
|
@@ -191,6 +211,57 @@ export const getAddFooterText = (commands: Commands) => {
|
|
|
191
211
|
};
|
|
192
212
|
};
|
|
193
213
|
|
|
214
|
+
export const getAddFlag =
|
|
215
|
+
(commandFactory: CommandFactory, commandsCollection: CommandsCollection) =>
|
|
216
|
+
(name: string, shortFlag?: string, description?: string): CommandFactory => {
|
|
217
|
+
const def: OptionDefinition = {
|
|
218
|
+
name,
|
|
219
|
+
shortFlag,
|
|
220
|
+
type: "boolean",
|
|
221
|
+
description: description || "",
|
|
222
|
+
};
|
|
223
|
+
commandsCollection[commandFactory.name].optionDefinitions.push(def);
|
|
224
|
+
return commandFactory;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export const getAddStringOption =
|
|
228
|
+
(commandFactory: CommandFactory, commandsCollection: CommandsCollection) =>
|
|
229
|
+
(
|
|
230
|
+
name: string,
|
|
231
|
+
shortFlag?: string,
|
|
232
|
+
description?: string,
|
|
233
|
+
config?: Partial<OptionDefinition>,
|
|
234
|
+
): CommandFactory => {
|
|
235
|
+
const def: OptionDefinition = {
|
|
236
|
+
name,
|
|
237
|
+
shortFlag,
|
|
238
|
+
type: "string",
|
|
239
|
+
description: description || "",
|
|
240
|
+
...config,
|
|
241
|
+
};
|
|
242
|
+
commandsCollection[commandFactory.name].optionDefinitions.push(def);
|
|
243
|
+
return commandFactory;
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
export const getAddNumberOption =
|
|
247
|
+
(commandFactory: CommandFactory, commandsCollection: CommandsCollection) =>
|
|
248
|
+
(
|
|
249
|
+
name: string,
|
|
250
|
+
shortFlag?: string,
|
|
251
|
+
description?: string,
|
|
252
|
+
config?: Partial<OptionDefinition>,
|
|
253
|
+
): CommandFactory => {
|
|
254
|
+
const def: OptionDefinition = {
|
|
255
|
+
name,
|
|
256
|
+
shortFlag,
|
|
257
|
+
type: "number",
|
|
258
|
+
description: description || "",
|
|
259
|
+
...config,
|
|
260
|
+
};
|
|
261
|
+
commandsCollection[commandFactory.name].optionDefinitions.push(def);
|
|
262
|
+
return commandFactory;
|
|
263
|
+
};
|
|
264
|
+
|
|
194
265
|
export const getGetHelpLine =
|
|
195
266
|
(command: Command) =>
|
|
196
267
|
(width: number = 50) => {
|
package/src/getStart.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import type { CommandsCollection } from "./types";
|
|
1
|
+
import type { CommandsCollection } from "./types.js";
|
|
2
|
+
import { parseArgs } from "./parseArgs.js";
|
|
3
|
+
import { applyDefaults } from "./applyDefaults.js";
|
|
4
|
+
import { validateOptions } from "./validateOptions.js";
|
|
2
5
|
|
|
3
6
|
export const getStart =
|
|
4
7
|
(commandsCollection: CommandsCollection) => (argv?: string[]) => {
|
|
5
8
|
if (!argv) argv = process.argv;
|
|
6
9
|
if (!Array.isArray(argv)) throw new Error("no arguments passed");
|
|
7
|
-
if (argv.length < 2) throw Error("arguments out of range");
|
|
10
|
+
if (argv.length < 2) throw new Error("arguments out of range");
|
|
8
11
|
argv = argv.slice(2);
|
|
9
12
|
if (argv.length === 0) {
|
|
10
13
|
commandsCollection.help.callback();
|
|
@@ -15,39 +18,31 @@ export const getStart =
|
|
|
15
18
|
|
|
16
19
|
const command = commandsCollection[commandName];
|
|
17
20
|
if (!command) {
|
|
18
|
-
console
|
|
21
|
+
console.log(`command '${commandName}' not found`);
|
|
19
22
|
return;
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
.filter((arg) => /\=/g.test(arg))
|
|
25
|
-
.map((arg) => arg.split("="))
|
|
26
|
-
.reduce((p: any, c) => {
|
|
27
|
-
p[c[0]] = c[1];
|
|
25
|
+
const parsedResult = parseArgs(argv.slice(1), command);
|
|
26
|
+
let options = parsedResult.options;
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
}, {});
|
|
28
|
+
options = applyDefaults(options, command);
|
|
31
29
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
(arg, n) => (options[command.positionedArguments[n]] = arg)
|
|
39
|
-
);
|
|
30
|
+
const validation = validateOptions(options, command);
|
|
31
|
+
if (!validation.valid) {
|
|
32
|
+
for (const error of validation.errors) {
|
|
33
|
+
console.log(error);
|
|
34
|
+
}
|
|
35
|
+
return;
|
|
40
36
|
}
|
|
41
37
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
.join(", ")}' not defined`
|
|
38
|
+
const positionedOverflow = parsedResult.positional.filter(
|
|
39
|
+
(arg) =>
|
|
40
|
+
!command.positionedArguments.some((name) => options[name] === arg),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
if (positionedOverflow.length > 0) {
|
|
44
|
+
console.log(
|
|
45
|
+
`argument${positionedOverflow.length === 1 ? "" : "s"} '${positionedOverflow.join(", ")}' not defined`,
|
|
51
46
|
);
|
|
52
47
|
return;
|
|
53
48
|
}
|
package/src/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { getCommands } from "./getCommandsFuncs";
|
|
1
|
+
export { getCommands } from "./getCommandsFuncs.js";
|