Skip to content

Commit

Permalink
Add repeat option
Browse files Browse the repository at this point in the history
  • Loading branch information
febeling committed Dec 7, 2023
1 parent 04fc547 commit 249ea6b
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 9 deletions.
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[![Node.js CI](https://github.com/febeling/synopt/actions/workflows/node.js.yml/badge.svg)](https://github.com/febeling/synopt/actions/workflows/node.js.yml)

# SYNOPT

Command line options package with narrow scope and ease of use.
Expand All @@ -13,6 +14,7 @@ Command line options package with narrow scope and ease of use.
## More Features

- support boolean options (flags)
- support repeat options (`-n first -n second,third` to `["first", "second", "third"]`)
- long and short option names (e.g. `-s`, `--long`)
- no assumptions about defaults (leave room for config files, and env vars)
- no assumptions about subcommands (but easy to accomodate)
Expand All @@ -22,24 +24,24 @@ Command line options package with narrow scope and ease of use.
## Example Usage

```js
import synopt from 'synopt';
import synopt from "synopt";

// Declare options
synopt
.name('mkwebmanifest') // optional, for usage banner
.name("mkwebmanifest") // optional, for usage banner
.summary("Generate icons and web manifest for web applications")
.option("-i", "--icon", "source icon file")
.option("-n", "--name", "name of the web application")
.option("-n", "--name", "name of the web application", { repeat: true })
.option("--config", "configuration file")
.option("--outdir", "directory path for generated files")
.option("--verbose", "print more information the console", { boolean: true })
.option("-h", "--help", "print information about options", { boolean: true });
.option("--verbose", "more output", { boolean: true })
.option("-h", "--help", "print help", { boolean: true });

// Slice off node executable and script
const argv = process.argv.slice(2);

// Destructure result
const { ok, error, options } = synopt.parse(argv)
const { ok, error, options } = synopt.parse(argv);

if (ok) {
main(options);
Expand Down
12 changes: 12 additions & 0 deletions src/synopt-declare.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,15 @@ test("boolean", () => {
},
]);
});

test("repeat option", () => {
synopt.option("--domain NAME", { repeat: true });
expect(synopt.declarations()).toEqual([
{
name: "domain",
argname: "NAME",
long: "--domain",
repeat: true,
},
]);
});
12 changes: 12 additions & 0 deletions src/synopt-parse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,15 @@ test("missing value (next is option short or long)", () => {
`Option '--name' requires value, because it's not boolean flag`,
);
});

test("repeat options to array", () => {
synopt.option("--domain NAME", { repeat: true });
const { ok, options } = synopt.parse([
"--domain",
"brilliant",
"--domain",
"logical,wrong",
]);
expect(ok).toBe(true);
expect(options).toEqual({ domain: ["brilliant", "logical", "wrong"] });
});
18 changes: 15 additions & 3 deletions src/synopt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface Command {

interface OptionDeclaration {
boolean?: boolean;
repeat?: boolean;
short?: string;
argname?: string;
long?: string;
Expand All @@ -25,6 +26,7 @@ interface CommandState {

interface DeclarationOptions {
boolean?: boolean;
repeat?: boolean;
}

type DeclarationTuple = [
Expand All @@ -35,7 +37,7 @@ type DeclarationTuple = [
];

type Options = {
[key: string]: string | boolean;
[key: string]: boolean | string | string[];
};

type ErrorResult = { ok: false; error: string };
Expand Down Expand Up @@ -67,9 +69,12 @@ const parseDeclaration = (declaration: DeclarationTuple): OptionDeclaration => {
while (decl.length > 0) {
const elem = decl.pop();
if (decl.length === 0 && typeof elem === "object") {
if ((elem as DeclarationOptions).boolean === true) {
if ((elem as DeclarationOptions).boolean) {
option.boolean = true;
}
if ((elem as DeclarationOptions).repeat) {
option.repeat = true;
}
} else {
const str = elem as string;
if (reShort.test(str)) {
Expand Down Expand Up @@ -143,7 +148,14 @@ const createCommand = (name?: string): Command => {
} else if (elementDecl.boolean) {
options[elementDecl.name] = true;
} else if (nextElement && !isOption(nextElement)) {
options[elementDecl.name] = nextElement;
if (elementDecl.repeat) {
options[elementDecl.name] ||= [];
(options[elementDecl.name] as string[]).push(
...nextElement.split(","),
);
} else {
options[elementDecl.name] = nextElement;
}
i++;
} else {
throw new Error(
Expand Down

0 comments on commit 249ea6b

Please sign in to comment.