Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@benev/argv

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@benev/argv - npm Package Compare versions

Comparing version 0.0.0-dev.2 to 0.0.0-dev.3

s/internals/testing/concept.ts

7

package.json
{
"name": "@benev/argv",
"version": "0.0.0-dev.2",
"version": "0.0.0-dev.3",
"description": "command line argument parser",

@@ -13,5 +13,2 @@ "license": "MIT",

],
"bin": {
"argv-echo": "x/echo.js"
},
"scripts": {

@@ -23,3 +20,3 @@ "build": "run-s clean compile",

"watch": "tsc -w",
"test": "exit 0"
"test": "node x/internals/testing/suite.test.js"
},

@@ -26,0 +23,0 @@ "devDependencies": {

@@ -7,4 +7,4 @@

🤖 for making node cli programs
🕵️‍♂️ designed for proper typescript typings
🏗️ experimental design, breaking changes likely
💁 autogenerated `--help` page
🕵️‍♂️ designed for good typescript typings
🧼 zero dependencies

@@ -15,5 +15,15 @@ 💖 made free and open source, just for you

## argv instructions
## example generated help page
1. install via npm
```sh
$ icecream waffle-cone --flavor cookie-dough --scoops 5
```
![example help output](assets/help.png)
<br/>
## instructions
1. install `@benev/argv` via npm
```sh

@@ -29,10 +39,9 @@ npm install @benev/argv

export type Args = {
environment: string
suite: string
vessel: string
}
export type Params = {
"--label": string
"--verbose": boolean
"--port": number
"--flavor": string
"--scoops": number
"--help": boolean
}

@@ -43,34 +52,16 @@ ```

const {args, params} = cli<Args, Params>()({
// your program's name
bin: "myprogram",
// process.argv is a nodejs builtin
program: "icecream",
argv: process.argv,
columns: process.stdout.columns ?? 72,
// terminal width, used for text-wrapping
columns: process.stdout.columns,
// link to your readme, for +help
readme: "https://github.com/@benev/argv",
// explainer for your menu, for +help
help: "my first command line program!",
// positional arguments your program will accept
argorder: ["currency", "amount"],
argorder: ["vessel"],
// arguments your program will accept
args: {
currency: {
vessel: {
type: String,
mode: "requirement",
help: "currency, like 'usd' or 'cad'",
help: 'can be "cone", "bowl", or "waffle-cone"',
},
amount: {
type: Number,
mode: "default",
default: 123,
help: "amount of money",
},
},

@@ -80,18 +71,18 @@

params: {
"--label": {
"--flavor": {
type: String,
mode: "option",
help: "a cool title",
mode: "default",
default: "vanilla",
help: "your favorite icecream flavor",
},
"--verbose": {
"--scoops": {
type: Number,
mode: "requirement",
help: "number of icecream scoops",
},
"--help": {
type: Boolean,
mode: "option",
help: "display additional information",
help: "trigger the help page",
},
"--port": {
type: Number,
mode: "default",
default: 8021,
help: "tcp port server will listen on",
},
},

@@ -103,18 +94,12 @@ })

// example command:
// main.js usd +verbose --port 8021
// $ icecream waffle-cone --flavor cookie-dough --scoops 5
args.currency
// "usd"
args.vessel
// "waffle-cone"
args.amount
// 123
params["--flavor"]
// "cookie-dough"
params["--label"]
// undefined
params["--verbose"]
// true
params["--port"]
// 8021
params["--scoops"]
// 5
```

@@ -141,1 +126,10 @@

- `"enabled"`
- `--help` has magic handling: it's the only "void" parameter which doesn't expect to be followed by a value
- eg, `icecream --help` is good
- eg, `icecream --help true` is bad
- understand that --help is the ONLY parameter treated this way
- all other parameters require a value
- parameter syntax is strict like this so that it's consistent and unambiguous
- however people will panic if `--help` doesn't give the expected result, thus the magic handling here
- use `+param` as a shorthand for enabling a boolean
- `+help` also works

@@ -10,5 +10,13 @@

import {applyDefaults} from "./internals/parsing/apply-defaults.js"
import {ConsoleLogger, Logger} from "./internals/testing/utils/logger.js"
import {validateRequirements} from "./internals/parsing/validate-requirements.js"
export function cli<A extends Values, P extends Values>() {
export function cli<A extends Values, P extends Values>({
logger = new ConsoleLogger(),
exit = code => process.exit(code),
}: {
logger?: Logger
exit?: (code: number) => void
} = {}) {
return function<

@@ -22,7 +30,7 @@ FA extends Field.GroupFromValues<A>,

if ("--help" in command.params && command.params["--help"]) {
if ("--help" in command.params) {
for (const report of helper(command))
console.log(report)
logger.log(report)
process.exit(0)
return <any>exit(0)
}

@@ -40,14 +48,14 @@

const errortext = errorReport(err)
const printError = () => console.error(...errortext)
const printError = () => logger.error(errortext.join(" "))
printError()
console.error("")
logger.error("")
for (const report of helper({spec}))
console.error(report)
logger.error(report)
console.error("")
logger.error("")
printError()
process.exit(1)
return <any>exit(1)
}
}
}
export * from "./cli.js"
export * from "./theme.js"

@@ -4,0 +5,0 @@ export * from "./errors/argv-error.js"

@@ -0,1 +1,2 @@

import {Theme} from "../theme.js"

@@ -13,1 +14,19 @@ export const stdcolumns = 72

]
export function makeTips(theme: Theme) {
const synonyms = affirmatives
.filter(s => s !== "true")
.map(s => theme.value(s))
.join("/")
const lines = [
`${theme.param("--param value")} is same as ${theme.param(`--param="value"`)}`,
`${theme.param("+param")} is shorthand for ${theme.param("--param=true")}`,
`${theme.value("true")} has synonyms ${synonyms}`
]
return lines
.map(s => theme.tip("~ ") + s)
.join("\n")
}
import {Spec} from "../types/spec.js"
import {Field} from "../types/field.js"
import {stdcolumns} from "./constants.js"
import {makeTips, stdcolumns} from "./constants.js"
import {Values} from "../types/values.js"
import {palette} from "./helping/palette.js"
import {stdtheme} from "../theme.js"
import {textblock} from "./helping/textblock.js"

@@ -21,2 +21,4 @@ import {fieldReport} from "./helping/field-report.js"

const theme = spec.theme ?? stdtheme
const tips = spec.tips ?? true

@@ -26,10 +28,10 @@ const columns = (spec.columns ?? stdcolumns) - 4

yield palette.binary(spec.bin) + " " + (
yield theme.binary(spec.program) + " " + (
argorder
.map(a => palette.arg(`<${a}>`))
.map(a => theme.arg(`<${a}>`))
.join(" ")
) + palette.param(" {parameters}")
) + theme.param(" {parameters}")
if (spec.readme)
yield palette.readme(" readme ") + palette.link(spec.readme)
yield theme.readme(" readme ") + theme.link(spec.readme)

@@ -47,5 +49,6 @@ if (spec.help)

columns,
theme,
field: spec.args[name],
value: retrieveValue(args, name),
color: palette.arg,
color: theme.arg,
})

@@ -56,6 +59,7 @@

name,
field,
columns,
field,
theme,
value: retrieveValue(params, name),
color: palette.param,
color: theme.param,
})

@@ -65,14 +69,9 @@

yield ""
yield palette.tip("tips")
yield theme.tip("tips")
yield textblock({
columns,
indent: [2, " "],
text:
[
`"${palette.param("+param")}" is short for "${palette.param("--param=true")}"`,
]
.map(s => palette.tip("~ ") + s)
.join("\n"),
text: makeTips(theme),
})
}
}
import {val} from "./val.js"
import {palette} from "./palette.js"
import {Theme} from "../../theme.js"
import {Type} from "../../types/type.js"
import {Field} from "../../types/field.js"
export function defaultValue(field: Field.Default<Type>) {
export function defaultValue(theme: Theme, field: Field.Default<Type>) {
return "default" in field
? " " + palette.value(val(field.default))
? " " + theme.value(val(field.default))
: ""
}

@@ -5,3 +5,3 @@

import {stype} from "./stype.js"
import {palette} from "./palette.js"
import {Theme} from "../../theme.js"
import {textblock} from "./textblock.js"

@@ -17,2 +17,3 @@ import {Type} from "../../types/type.js"

value,
theme,
color,

@@ -22,2 +23,3 @@ }: {

columns: number
theme: Theme
field: Field.Any<Type>

@@ -35,7 +37,7 @@ value: undefined | {v: any}

text: `${color(name)}`
+ ` ${mode(field.mode)}`
+ ` ${stype(field.type)}`
+ `${defaultValue(<any>field)}`
+ ` ${mode(theme, field.mode)}`
+ ` ${theme.type(stype(field.type))}`
+ `${defaultValue(theme, <any>field)}`
+ (value
? " " + palette.detected("got ") + palette.value(val(value.v))
? " " + theme.detected("got ") + theme.value(val(value.v))
: "")

@@ -42,0 +44,0 @@ ,

import {palette} from "./palette.js"
import {Theme} from "../../theme.js"
import {Field} from "../../types/field.js"
export function mode(mode: Field.Mode) {
export function mode(theme: Theme, mode: Field.Mode) {
switch (mode) {
case "requirement":
return palette.required("required")
return theme.required("required")
case "option":
return palette.mode("optional")
return theme.mode("optional")
case "default":
return palette.mode("default")
return theme.mode("default")
}
}
import {palette} from "./palette.js"
import {Type} from "../../types/type.js"

@@ -9,10 +8,13 @@

case String:
return palette.type("string")
return "string"
case Number:
return palette.type("number")
return "number"
case Boolean:
return palette.type("boolean")
return "boolean"
default:
return "unknown"
}
}

@@ -21,5 +21,6 @@

saveArg,
saveParam,
saveParamTrue,
saveScheduledParam,
saveEqualSignedParam,
saveEnabledBooleanParam,
saveShorthandBoolean,
scheduledParamAssignment,

@@ -31,3 +32,3 @@ scheduleNextItemAsParamValue,

if (scheduledParamAssignment())
saveParam(item)
saveScheduledParam(item)
else {

@@ -38,5 +39,8 @@ if (item.startsWith("--"))

else
scheduleNextItemAsParamValue(item)
if (item === "--help")
saveParamTrue(item)
else
scheduleNextItemAsParamValue(item)
else if (item.startsWith("+"))
saveEnabledBooleanParam(item)
saveShorthandBoolean(item)
else

@@ -43,0 +47,0 @@ saveArg(item)

@@ -42,3 +42,7 @@

saveParam: (item: string) => {
saveParamTrue: (item: string) => {
params[<keyof FP>item] = <any>true
},
saveScheduledParam: (item: string) => {
const name = scheduledParamAssignment!

@@ -60,3 +64,3 @@ scheduledParamAssignment = undefined

saveEnabledBooleanParam(item: string) {
saveShorthandBoolean(item: string) {
const name = "--" + item.slice(1)

@@ -63,0 +67,0 @@ params[<keyof FP>name] = <any>true

@@ -20,3 +20,3 @@

.map(([key, code]) => [
key,
key,
(s: string) => `${code}${s}${codes.reset}`,

@@ -23,0 +23,0 @@ ])

import {Field} from "./field.js"
import {Theme} from "../theme.js"

@@ -8,11 +9,32 @@ export interface Spec<

> {
bin: string
/** the name of your program's executable */
program: string
/** command line arguments (in node, use process.argv) */
argv: string[]
/** positional arguments your program will accept */
argorder: (keyof FA)[]
/** arguments specification */
args: FA
/** parameters specification */
params: FP
/** url to your program's readme */
readme?: string
/** description and usage instructions for your program */
help?: string
/** current terminal width, used for text-wrapping */
columns?: number
/** display "tips" section at end of +help page */
tips?: boolean
/** color palette to use in the +help page */
theme?: Theme
}

@@ -5,2 +5,6 @@ import { Spec } from "./types/spec.js";

import { Command } from "./types/command.js";
export declare function cli<A extends Values, P extends Values>(): <FA extends Field.GroupFromValues<A>, FP extends Field.GroupFromValues<P>>(spec: Spec<FA, FP>) => Command<FA, FP>;
import { Logger } from "./internals/testing/utils/logger.js";
export declare function cli<A extends Values, P extends Values>({ logger, exit, }?: {
logger?: Logger;
exit?: (code: number) => void;
}): <FA extends Field.GroupFromValues<A>, FP extends Field.GroupFromValues<P>>(spec: Spec<FA, FP>) => Command<FA, FP>;

@@ -5,11 +5,12 @@ import { parse } from "./internals/parse.js";

import { applyDefaults } from "./internals/parsing/apply-defaults.js";
import { ConsoleLogger } from "./internals/testing/utils/logger.js";
import { validateRequirements } from "./internals/parsing/validate-requirements.js";
export function cli() {
export function cli({ logger = new ConsoleLogger(), exit = code => process.exit(code), } = {}) {
return function (spec) {
try {
const command = parse(spec);
if ("--help" in command.params && command.params["--help"]) {
if ("--help" in command.params) {
for (const report of helper(command))
console.log(report);
process.exit(0);
logger.log(report);
return exit(0);
}

@@ -24,10 +25,10 @@ validateRequirements(command.spec.args, command.args);

const errortext = errorReport(err);
const printError = () => console.error(...errortext);
const printError = () => logger.error(errortext.join(" "));
printError();
console.error("");
logger.error("");
for (const report of helper({ spec }))
console.error(report);
console.error("");
logger.error(report);
logger.error("");
printError();
process.exit(1);
return exit(1);
}

@@ -34,0 +35,0 @@ };

export * from "./cli.js";
export * from "./theme.js";
export * from "./errors/argv-error.js";

@@ -3,0 +4,0 @@ export * from "./tools/colors.js";

export * from "./cli.js";
export * from "./theme.js";
export * from "./errors/argv-error.js";

@@ -3,0 +4,0 @@ export * from "./tools/colors.js";

@@ -0,3 +1,5 @@

import { Theme } from "../theme.js";
export declare const stdcolumns = 72;
export declare const mincolumns = 10;
export declare const affirmatives: string[];
export declare function makeTips(theme: Theme): string;

@@ -11,2 +11,16 @@ export const stdcolumns = 72;

];
export function makeTips(theme) {
const synonyms = affirmatives
.filter(s => s !== "true")
.map(s => theme.value(s))
.join("/");
const lines = [
`${theme.param("--param value")} is same as ${theme.param(`--param="value"`)}`,
`${theme.param("+param")} is shorthand for ${theme.param("--param=true")}`,
`${theme.value("true")} has synonyms ${synonyms}`
];
return lines
.map(s => theme.tip("~ ") + s)
.join("\n");
}
//# sourceMappingURL=constants.js.map

@@ -1,3 +0,3 @@

import { stdcolumns } from "./constants.js";
import { palette } from "./helping/palette.js";
import { makeTips, stdcolumns } from "./constants.js";
import { stdtheme } from "../theme.js";
import { textblock } from "./helping/textblock.js";

@@ -7,11 +7,12 @@ import { fieldReport } from "./helping/field-report.js";

export function* helper({ spec, args = {}, params = {}, }) {
var _a, _b;
const tips = (_a = spec.tips) !== null && _a !== void 0 ? _a : true;
const columns = ((_b = spec.columns) !== null && _b !== void 0 ? _b : stdcolumns) - 4;
var _a, _b, _c;
const theme = (_a = spec.theme) !== null && _a !== void 0 ? _a : stdtheme;
const tips = (_b = spec.tips) !== null && _b !== void 0 ? _b : true;
const columns = ((_c = spec.columns) !== null && _c !== void 0 ? _c : stdcolumns) - 4;
const argorder = spec.argorder;
yield palette.binary(spec.bin) + " " + (argorder
.map(a => palette.arg(`<${a}>`))
.join(" ")) + palette.param(" {parameters}");
yield theme.binary(spec.program) + " " + (argorder
.map(a => theme.arg(`<${a}>`))
.join(" ")) + theme.param(" {parameters}");
if (spec.readme)
yield palette.readme(" readme ") + palette.link(spec.readme);
yield theme.readme(" readme ") + theme.link(spec.readme);
if (spec.help)

@@ -27,5 +28,6 @@ yield textblock({

columns,
theme,
field: spec.args[name],
value: retrieveValue(args, name),
color: palette.arg,
color: theme.arg,
});

@@ -35,18 +37,15 @@ for (const [name, field] of Object.entries(spec.params))

name,
field,
columns,
field,
theme,
value: retrieveValue(params, name),
color: palette.param,
color: theme.param,
});
if (tips) {
yield "";
yield palette.tip("tips");
yield theme.tip("tips");
yield textblock({
columns,
indent: [2, " "],
text: [
`"${palette.param("+param")}" is short for "${palette.param("--param=true")}"`,
]
.map(s => palette.tip("~ ") + s)
.join("\n"),
text: makeTips(theme),
});

@@ -53,0 +52,0 @@ }

@@ -0,3 +1,4 @@

import { Theme } from "../../theme.js";
import { Type } from "../../types/type.js";
import { Field } from "../../types/field.js";
export declare function defaultValue(field: Field.Default<Type>): string;
export declare function defaultValue(theme: Theme, field: Field.Default<Type>): string;
import { val } from "./val.js";
import { palette } from "./palette.js";
export function defaultValue(field) {
export function defaultValue(theme, field) {
return "default" in field
? " " + palette.value(val(field.default))
? " " + theme.value(val(field.default))
: "";
}
//# sourceMappingURL=default-value.js.map

@@ -0,6 +1,8 @@

import { Theme } from "../../theme.js";
import { Type } from "../../types/type.js";
import { Field } from "../../types/field.js";
export declare function fieldReport({ name, columns, field, value, color, }: {
export declare function fieldReport({ name, columns, field, value, theme, color, }: {
name: string;
columns: number;
theme: Theme;
field: Field.Any<Type>;

@@ -7,0 +9,0 @@ value: undefined | {

import { val } from "./val.js";
import { mode } from "./mode.js";
import { stype } from "./stype.js";
import { palette } from "./palette.js";
import { textblock } from "./textblock.js";
import { defaultValue } from "./default-value.js";
export function fieldReport({ name, columns, field, value, color, }) {
export function fieldReport({ name, columns, field, value, theme, color, }) {
let report = "";

@@ -14,7 +13,7 @@ report += "\n";

text: `${color(name)}`
+ ` ${mode(field.mode)}`
+ ` ${stype(field.type)}`
+ `${defaultValue(field)}`
+ ` ${mode(theme, field.mode)}`
+ ` ${theme.type(stype(field.type))}`
+ `${defaultValue(theme, field)}`
+ (value
? " " + palette.detected("got ") + palette.value(val(value.v))
? " " + theme.detected("got ") + theme.value(val(value.v))
: ""),

@@ -21,0 +20,0 @@ });

@@ -0,2 +1,3 @@

import { Theme } from "../../theme.js";
import { Field } from "../../types/field.js";
export declare function mode(mode: Field.Mode): string;
export declare function mode(theme: Theme, mode: Field.Mode): string;

@@ -1,12 +0,11 @@

import { palette } from "./palette.js";
export function mode(mode) {
export function mode(theme, mode) {
switch (mode) {
case "requirement":
return palette.required("required");
return theme.required("required");
case "option":
return palette.mode("optional");
return theme.mode("optional");
case "default":
return palette.mode("default");
return theme.mode("default");
}
}
//# sourceMappingURL=mode.js.map
import { Type } from "../../types/type.js";
export declare function stype(type: Type): string | undefined;
export declare function stype(type: Type): "string" | "number" | "boolean" | "unknown";

@@ -1,12 +0,13 @@

import { palette } from "./palette.js";
export function stype(type) {
switch (type) {
case String:
return palette.type("string");
return "string";
case Number:
return palette.type("number");
return "number";
case Boolean:
return palette.type("boolean");
return "boolean";
default:
return "unknown";
}
}
//# sourceMappingURL=stype.js.map

@@ -7,6 +7,6 @@ import { parsingMachine } from "./parsing/machine.js";

const [executable, module, ...items] = spec.argv;
const { args, params, saveArg, saveParam, saveEqualSignedParam, saveEnabledBooleanParam, scheduledParamAssignment, scheduleNextItemAsParamValue, } = parsingMachine(spec);
const { args, params, saveArg, saveParamTrue, saveScheduledParam, saveEqualSignedParam, saveShorthandBoolean, scheduledParamAssignment, scheduleNextItemAsParamValue, } = parsingMachine(spec);
for (const item of items) {
if (scheduledParamAssignment())
saveParam(item);
saveScheduledParam(item);
else {

@@ -16,6 +16,8 @@ if (item.startsWith("--"))

saveEqualSignedParam(item);
else if (item === "--help")
saveParamTrue(item);
else
scheduleNextItemAsParamValue(item);
else if (item.startsWith("+"))
saveEnabledBooleanParam(item);
saveShorthandBoolean(item);
else

@@ -22,0 +24,0 @@ saveArg(item);

@@ -9,6 +9,7 @@ import { Spec } from "../../types/spec.js";

scheduleNextItemAsParamValue: (item: string) => void;
saveParam: (item: string) => void;
saveParamTrue: (item: string) => void;
saveScheduledParam: (item: string) => void;
saveArg: (item: string) => void;
saveEqualSignedParam(item: string): void;
saveEnabledBooleanParam(item: string): void;
saveShorthandBoolean(item: string): void;
};

@@ -26,3 +26,6 @@ import { parseValue } from "./parse-value.js";

},
saveParam: (item) => {
saveParamTrue: (item) => {
params[item] = true;
},
saveScheduledParam: (item) => {
const name = scheduledParamAssignment;

@@ -41,3 +44,3 @@ scheduledParamAssignment = undefined;

},
saveEnabledBooleanParam(item) {
saveShorthandBoolean(item) {
const name = "--" + item.slice(1);

@@ -44,0 +47,0 @@ params[name] = true;

import { Field } from "./field.js";
import { Theme } from "../theme.js";
export interface Spec<FA extends Field.Group = Field.Group, FP extends Field.Group = Field.Group> {
bin: string;
/** the name of your program's executable */
program: string;
/** command line arguments (in node, use process.argv) */
argv: string[];
/** positional arguments your program will accept */
argorder: (keyof FA)[];
/** arguments specification */
args: FA;
/** parameters specification */
params: FP;
/** url to your program's readme */
readme?: string;
/** description and usage instructions for your program */
help?: string;
/** current terminal width, used for text-wrapping */
columns?: number;
/** display "tips" section at end of +help page */
tips?: boolean;
/** color palette to use in the +help page */
theme?: Theme;
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc