summaryrefslogtreecommitdiff
path: root/std/flags/mod.ts
diff options
context:
space:
mode:
Diffstat (limited to 'std/flags/mod.ts')
-rw-r--r--std/flags/mod.ts211
1 files changed, 122 insertions, 89 deletions
diff --git a/std/flags/mod.ts b/std/flags/mod.ts
index 9bc4e8cdf..59cae5d15 100644
--- a/std/flags/mod.ts
+++ b/std/flags/mod.ts
@@ -1,29 +1,62 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
import { assert } from "../testing/asserts.ts";
-// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+export interface Args {
+ /** Contains all the arguments that didn't have an option associated with
+ * them. */
+ _: Array<string | number>;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ [key: string]: any;
+}
+
export interface ArgParsingOptions {
- unknown: (i: unknown) => unknown;
- boolean: boolean | string | string[];
- alias: { [key: string]: string | string[] };
- string: string | string[];
- default: { [key: string]: unknown };
+ /** When `true`, populate the result `_` with everything before the `--` and
+ * the result `['--']` with everything after the `--`. Here's an example:
+ *
+ * const { args } = Deno;
+ * import { parse } from "https://deno.land/std/flags/mod.ts";
+ * // options['--'] is now set to false
+ * console.dir(parse(args, { "--": false }));
+ * // $ deno example.ts -- a arg1
+ * // output: { _: [ "example.ts", "a", "arg1" ] }
+ * // options['--'] is now set to true
+ * console.dir(parse(args, { "--": true }));
+ * // $ deno example.ts -- a arg1
+ * // output: { _: [ "example.ts" ], --: [ "a", "arg1" ] }
+ *
+ * Defaults to `false`.
+ */
"--": boolean;
+
+ /** An object mapping string names to strings or arrays of string argument
+ * names to use as aliases */
+ alias: Record<string, string | string[]>;
+
+ /** A boolean, string or array of strings to always treat as booleans. If
+ * `true` will treat all double hyphenated arguments without equal signs as
+ * `boolean` (e.g. affects `--foo`, not `-f` or `--foo=bar`) */
+ boolean: boolean | string | string[];
+
+ /** An object mapping string argument names to default values. */
+ default: Record<string, unknown>;
+
+ /** When `true`, populate the result `_` with everything after the first
+ * non-option. */
stopEarly: boolean;
-}
-const DEFAULT_OPTIONS = {
- unknown: (i: unknown): unknown => i,
- boolean: false,
- alias: {},
- string: [],
- default: {},
- "--": false,
- stopEarly: false
-};
+ /** A string or array of strings argument names to always treat as strings. */
+ string: string | string[];
+
+ /** A function which is invoked with a command line parameter not defined in
+ * the `options` configuration object. If the function returns `false`, the
+ * unknown option is not added to `parsedArgs`. */
+ unknown: (i: unknown) => unknown;
+}
interface Flags {
- bools: { [key: string]: boolean };
- strings: { [key: string]: boolean };
+ bools: Record<string, boolean>;
+ strings: Record<string, boolean>;
unknownFn: (i: unknown) => unknown;
allBools: boolean;
}
@@ -32,12 +65,13 @@ interface NestedMapping {
[key: string]: NestedMapping | unknown;
}
-function get<T>(obj: { [s: string]: T }, key: string): T | undefined {
+function get<T>(obj: Record<string, T>, key: string): T | undefined {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
return obj[key];
}
}
-function getForce<T>(obj: { [s: string]: T }, key: string): T {
+
+function getForce<T>(obj: Record<string, T>, key: string): T {
const v = get(obj, key);
assert(v != null);
return v;
@@ -51,82 +85,80 @@ function isNumber(x: unknown): boolean {
function hasKey(obj: NestedMapping, keys: string[]): boolean {
let o = obj;
- keys.slice(0, -1).forEach(function(key: string): void {
- o = (get(o, key) || {}) as NestedMapping;
+ keys.slice(0, -1).forEach(key => {
+ o = (get(o, key) ?? {}) as NestedMapping;
});
const key = keys[keys.length - 1];
return key in o;
}
+/** Take a set of command line arguments, with an optional set of options, and
+ * return an object representation of those argument.
+ *
+ * const parsedArgs = parse(Deno.args);
+ */
export function parse(
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- args: any[],
- initialOptions: Partial<ArgParsingOptions> = {}
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
-): { [key: string]: any } {
- const options: ArgParsingOptions = {
- ...DEFAULT_OPTIONS,
- ...initialOptions
- };
-
+ args: string[],
+ {
+ "--": doubleDash = false,
+ alias = {},
+ boolean = false,
+ default: defaults = {},
+ stopEarly = false,
+ string = [],
+ unknown = (i: unknown): unknown => i
+ }: Partial<ArgParsingOptions> = {}
+): Args {
const flags: Flags = {
bools: {},
strings: {},
- unknownFn: options.unknown,
+ unknownFn: unknown,
allBools: false
};
- if (options.boolean !== undefined) {
- if (typeof options.boolean === "boolean") {
- flags.allBools = !!options.boolean;
+ if (boolean !== undefined) {
+ if (typeof boolean === "boolean") {
+ flags.allBools = !!boolean;
} else {
- const booleanArgs: string[] =
- typeof options.boolean === "string"
- ? [options.boolean]
- : options.boolean;
+ const booleanArgs = typeof boolean === "string" ? [boolean] : boolean;
- booleanArgs.filter(Boolean).forEach((key: string): void => {
+ for (const key of booleanArgs.filter(Boolean)) {
flags.bools[key] = true;
- });
+ }
}
}
- const aliases: { [key: string]: string[] } = {};
- if (options.alias !== undefined) {
- for (const key in options.alias) {
- const val = getForce(options.alias, key);
+ const aliases: Record<string, string[]> = {};
+ if (alias !== undefined) {
+ for (const key in alias) {
+ const val = getForce(alias, key);
if (typeof val === "string") {
aliases[key] = [val];
} else {
aliases[key] = val;
}
for (const alias of getForce(aliases, key)) {
- aliases[alias] = [key].concat(
- aliases[key].filter((y: string): boolean => alias !== y)
- );
+ aliases[alias] = [key].concat(aliases[key].filter(y => alias !== y));
}
}
}
- if (options.string !== undefined) {
- const stringArgs =
- typeof options.string === "string" ? [options.string] : options.string;
+ if (string !== undefined) {
+ const stringArgs = typeof string === "string" ? [string] : string;
- stringArgs.filter(Boolean).forEach(function(key): void {
+ for (const key of stringArgs.filter(Boolean)) {
flags.strings[key] = true;
const alias = get(aliases, key);
if (alias) {
- alias.forEach((alias: string): void => {
- flags.strings[alias] = true;
- });
+ for (const al of alias) {
+ flags.strings[al] = true;
+ }
}
- });
+ }
}
- const defaults = options.default;
-
- const argv: { [key: string]: unknown[] } = { _: [] };
+ const argv: Args = { _: [] };
function argDefined(key: string, arg: string): boolean {
return (
@@ -172,25 +204,28 @@ export function parse(
const value = !get(flags.strings, key) && isNumber(val) ? Number(val) : val;
setKey(argv, key.split("."), value);
- (get(aliases, key) || []).forEach(function(x): void {
- setKey(argv, x.split("."), value);
- });
+ const alias = get(aliases, key);
+ if (alias) {
+ for (const x of alias) {
+ setKey(argv, x.split("."), value);
+ }
+ }
}
function aliasIsBoolean(key: string): boolean {
- return getForce(aliases, key).some(function(x): boolean {
- return typeof get(flags.bools, x) === "boolean";
- });
+ return getForce(aliases, key).some(
+ x => typeof get(flags.bools, x) === "boolean"
+ );
}
- Object.keys(flags.bools).forEach(function(key): void {
+ for (const key of Object.keys(flags.bools)) {
setArg(key, defaults[key] === undefined ? false : defaults[key]);
- });
+ }
let notFlags: string[] = [];
// all args after "--" are not parsed
- if (args.indexOf("--") !== -1) {
+ if (args.includes("--")) {
notFlags = args.slice(args.indexOf("--") + 1);
args = args.slice(0, args.indexOf("--"));
}
@@ -199,13 +234,9 @@ export function parse(
const arg = args[i];
if (/^--.+=/.test(arg)) {
- // Using [\s\S] instead of . because js doesn't support the
- // 'dotall' regex modifier. See:
- // http://stackoverflow.com/a/1068308/13216
- const m = arg.match(/^--([^=]+)=([\s\S]*)$/);
+ const m = arg.match(/^--([^=]+)=(.*)$/s);
assert(m != null);
- const key = m[1];
- const value = m[2];
+ const [, key, value] = m;
if (flags.bools[key]) {
const booleanValue = value !== "false";
@@ -220,7 +251,7 @@ export function parse(
} else if (/^--.+/.test(arg)) {
const m = arg.match(/^--(.+)/);
assert(m != null);
- const key = m[1];
+ const [, key] = m;
const next = args[i + 1];
if (
next !== undefined &&
@@ -273,7 +304,7 @@ export function parse(
}
}
- const key = arg.slice(-1)[0];
+ const [key] = arg.slice(-1);
if (!broken && key !== "-") {
if (
args[i + 1] &&
@@ -292,34 +323,36 @@ export function parse(
}
} else {
if (!flags.unknownFn || flags.unknownFn(arg) !== false) {
- argv._.push(flags.strings["_"] || !isNumber(arg) ? arg : Number(arg));
+ argv._.push(flags.strings["_"] ?? !isNumber(arg) ? arg : Number(arg));
}
- if (options.stopEarly) {
+ if (stopEarly) {
argv._.push(...args.slice(i + 1));
break;
}
}
}
- Object.keys(defaults).forEach(function(key): void {
+ for (const key of Object.keys(defaults)) {
if (!hasKey(argv, key.split("."))) {
setKey(argv, key.split("."), defaults[key]);
- (aliases[key] || []).forEach(function(x): void {
- setKey(argv, x.split("."), defaults[key]);
- });
+ if (aliases[key]) {
+ for (const x of aliases[key]) {
+ setKey(argv, x.split("."), defaults[key]);
+ }
+ }
}
- });
+ }
- if (options["--"]) {
+ if (doubleDash) {
argv["--"] = [];
- notFlags.forEach(function(key): void {
+ for (const key of notFlags) {
argv["--"].push(key);
- });
+ }
} else {
- notFlags.forEach(function(key): void {
+ for (const key of notFlags) {
argv._.push(key);
- });
+ }
}
return argv;