Skip to content

Instantly share code, notes, and snippets.

@Segmentational
Last active August 30, 2022 08:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Segmentational/a8a00b54ddbdfb433842a941b8eb1fd0 to your computer and use it in GitHub Desktop.
Save Segmentational/a8a00b54ddbdfb433842a941b8eb1fd0 to your computer and use it in GitHub Desktop.
Node CLI Argument Parsing
import Utility from "util";
process.argv = [...process.argv, "hello", "--debug", "--secrets-manager=local-azure-test", "another", "--key", "value", "--debug"];
class Arguable extends String {
position?: number;
original?: string;
constructor(value: string | Arguable) {
super(value);
this.original = String(value);
this.position = process.argv.indexOf(this.original);
}
}
class Classifiable extends Arguable {
readonly type: "flag" | "positional" | "kv";
constructor(value: string | Arguable, type: "flag" | "positional" | "kv") {
super(value);
this.type = type;
}
}
/*** A CLI argument of which a (-/--) argument doesn't proceed */
class Positional extends Classifiable {
readonly value: string;
constructor(value: string | Arguable) {
super(value, "positional");
this.value = String(value);
}
}
/*** A CLI argument of which doesn't associate with a value assignment */
class Flag extends Classifiable {
readonly value: string;
override readonly position: number;
constructor(value: string | Arguable, position: number) {
super(value, "flag");
this.value = String(value).replace("--", "");
this.position = position;
};
}
class KV extends Classifiable {
readonly value: { key: string, assignment: string };
/***
*
* @param value
* @param singleton {@type boolean}
*/
constructor(value: Arguable, assignment?: { key: string | Argument, value: string | Argument }) {
super(value, "kv");
const key = ((this.singleton) ? String(this.original?.split("=")[0]) : String(this.original)).replace("--", "");
const attribution = (this.singleton) ? String(this.original?.split("=")[1]) : String(this.original);
this.value = {key, assignment: attribution};
if (assignment && "key" in assignment) {
this.value.key = String(assignment.key).replace("--", "");
}
if (assignment && "value" in assignment) {
this.value.assignment = String(assignment.value);
}
}
/***
* Boolean that represents if the given Argument was specified via a single string.
*
* @example
* $ node callable.js --key=value
* */
get singleton() {
return (this.original) ? (this.original.includes("=")) : false;
}
}
type Argument = Positional | Flag | KV;
type Arguments = (Argument)[];
export function Argument(comparator: Argument | string, iterator: number, parameters: string[] | Arguments): (Positional | Flag | KV) {
parameters = [...new Set((parameters as string[]))];
const length = (parameters.length - 1);
const index = parameters.indexOf(comparator as string);
const valid = (index !== -1);
if (!(valid)) {
const search = Utility.inspect(comparator, {
colors: true
});
const serial = Utility.inspect(parameters, {
colors: true,
compact: false
});
throw new Error("Invalid Input" + " " + "(" + search + ")" + " " + "for Array" + " " + serial);
}
const prefix = (String(comparator[0]) + String(comparator[1])).trim();
if (prefix === "--" || prefix === "-") {
// Narrows down to either a Flag || KV
if (comparator.includes("=")) {
// Explicitly narrows down to KV
return new KV(comparator);
}
/*** Further evaluation requires a counter */
const next = (parameters[iterator + 1] && (iterator + 1) <= length) ? parameters[iterator + 1] : null;
if (index !== length && (next) && !(next?.includes("--"))) {
// Next := Positional, Implicitly --> narrows down to KV
return new KV(comparator, {key: comparator, value: next as string});
}
// Pass-through narrows down to only a Flag
return new Flag(comparator, iterator);
}
// `previous` is heavily dependent on `next` -- apply special considerations around updates to either `previous` or `next`
const previous = (parameters[iterator - 1]) ? Argument(parameters[iterator - 1]!, iterator - 1, parameters) : null;
if (index === length && (previous instanceof Flag)) {
// Narrows down to KV
return new KV(comparator, {key: previous.value, value: comparator});
} else if ((previous instanceof KV) && !(previous.singleton)) {
// Narrows down to `typeof previous` := KV
return new KV(comparator, {key: previous.value.key, value: previous.value.assignment});
}
// Pass-through narrows down to Positional;
return new Positional(comparator);
}
function categorize() {
const parameters = process.argv.map(Argument);
console.log([...new Set(parameters)]);
}
categorize();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment