diff options
Diffstat (limited to 'std/flags')
m--------- | std | 0 | ||||
-rw-r--r-- | std/flags/README.md | 72 | ||||
-rwxr-xr-x | std/flags/all_bool_test.ts | 34 | ||||
-rwxr-xr-x | std/flags/bool_test.ts | 168 | ||||
-rwxr-xr-x | std/flags/dash_test.ts | 29 | ||||
-rwxr-xr-x | std/flags/default_bool_test.ts | 33 | ||||
-rwxr-xr-x | std/flags/dotted_test.ts | 24 | ||||
-rw-r--r-- | std/flags/example.ts | 5 | ||||
-rwxr-xr-x | std/flags/kv_short_test.ts | 14 | ||||
-rwxr-xr-x | std/flags/long_test.ts | 20 | ||||
-rw-r--r-- | std/flags/mod.ts | 321 | ||||
-rwxr-xr-x | std/flags/num_test.ts | 41 | ||||
-rwxr-xr-x | std/flags/parse_test.ts | 201 | ||||
-rwxr-xr-x | std/flags/short_test.ts | 46 | ||||
-rwxr-xr-x | std/flags/stop_early_test.ts | 16 | ||||
-rwxr-xr-x | std/flags/unknown_test.ts | 98 | ||||
-rwxr-xr-x | std/flags/whitespace_test.ts | 8 |
17 files changed, 1130 insertions, 0 deletions
diff --git a/std b/std deleted file mode 160000 -Subproject 43aafbf33285753e7b42230f0eb7969b300f71c diff --git a/std/flags/README.md b/std/flags/README.md new file mode 100644 index 000000000..41692b619 --- /dev/null +++ b/std/flags/README.md @@ -0,0 +1,72 @@ +# flags + +Command line arguments parser for Deno based on minimist + +# Example + +```ts +const { args } = Deno; +import { parse } from "https://deno.land/std/flags/mod.ts"; + +console.dir(parse(args)); +``` + +``` +$ deno example.ts -a beep -b boop +{ _: [], a: 'beep', b: 'boop' } +``` + +``` +$ deno example.ts -x 3 -y 4 -n5 -abc --beep=boop foo bar baz +{ _: [ 'foo', 'bar', 'baz' ], + x: 3, + y: 4, + n: 5, + a: true, + b: true, + c: true, + beep: 'boop' } +``` + +# API + +## const parsedArgs = parse(args, options = {}); + +`parsedArgs._` contains all the arguments that didn't have an option associated +with them. + +Numeric-looking arguments will be returned as numbers unless `options.string` or +`options.boolean` is set for that argument name. + +Any arguments after `'--'` will not be parsed and will end up in `parsedArgs._`. + +options can be: + +- `options.string` - a string or array of strings argument names to always treat + as strings +- `options.boolean` - 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`) +- `options.alias` - an object mapping string names to strings or arrays of + string argument names to use as aliases +- `options.default` - an object mapping string argument names to default values +- `options.stopEarly` - when true, populate `parsedArgs._` with everything after + the first non-option +- `options['--']` - when true, populate `parsedArgs._` with everything before + the `--` and `parsedArgs['--']` with everything after the `--`. Here's an + example: + ```ts + 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" ] } + ``` +- `options.unknown` - 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`. diff --git a/std/flags/all_bool_test.ts b/std/flags/all_bool_test.ts new file mode 100755 index 000000000..711bf3da1 --- /dev/null +++ b/std/flags/all_bool_test.ts @@ -0,0 +1,34 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; +import { parse } from "./mod.ts"; + +// flag boolean true (default all --args to boolean) +test(function flagBooleanTrue(): void { + const argv = parse(["moo", "--honk", "cow"], { + boolean: true + }); + + assertEquals(argv, { + honk: true, + _: ["moo", "cow"] + }); + + assertEquals(typeof argv.honk, "boolean"); +}); + +// flag boolean true only affects double hyphen arguments without equals signs +test(function flagBooleanTrueOnlyAffectsDoubleDash(): void { + const argv = parse(["moo", "--honk", "cow", "-p", "55", "--tacos=good"], { + boolean: true + }); + + assertEquals(argv, { + honk: true, + tacos: "good", + p: 55, + _: ["moo", "cow"] + }); + + assertEquals(typeof argv.honk, "boolean"); +}); diff --git a/std/flags/bool_test.ts b/std/flags/bool_test.ts new file mode 100755 index 000000000..422ae0933 --- /dev/null +++ b/std/flags/bool_test.ts @@ -0,0 +1,168 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; +import { parse } from "./mod.ts"; + +test(function flagBooleanDefaultFalse(): void { + const argv = parse(["moo"], { + boolean: ["t", "verbose"], + default: { verbose: false, t: false } + }); + + assertEquals(argv, { + verbose: false, + t: false, + _: ["moo"] + }); + + assertEquals(typeof argv.verbose, "boolean"); + assertEquals(typeof argv.t, "boolean"); +}); + +test(function booleanGroups(): void { + const argv = parse(["-x", "-z", "one", "two", "three"], { + boolean: ["x", "y", "z"] + }); + + assertEquals(argv, { + x: true, + y: false, + z: true, + _: ["one", "two", "three"] + }); + + assertEquals(typeof argv.x, "boolean"); + assertEquals(typeof argv.y, "boolean"); + assertEquals(typeof argv.z, "boolean"); +}); + +test(function booleanAndAliasWithChainableApi(): void { + const aliased = ["-h", "derp"]; + const regular = ["--herp", "derp"]; + const aliasedArgv = parse(aliased, { + boolean: "herp", + alias: { h: "herp" } + }); + const propertyArgv = parse(regular, { + boolean: "herp", + alias: { h: "herp" } + }); + const expected = { + herp: true, + h: true, + _: ["derp"] + }; + + assertEquals(aliasedArgv, expected); + assertEquals(propertyArgv, expected); +}); + +test(function booleanAndAliasWithOptionsHash(): void { + const aliased = ["-h", "derp"]; + const regular = ["--herp", "derp"]; + const opts = { + alias: { h: "herp" }, + boolean: "herp" + }; + const aliasedArgv = parse(aliased, opts); + const propertyArgv = parse(regular, opts); + const expected = { + herp: true, + h: true, + _: ["derp"] + }; + assertEquals(aliasedArgv, expected); + assertEquals(propertyArgv, expected); +}); + +test(function booleanAndAliasArrayWithOptionsHash(): void { + const aliased = ["-h", "derp"]; + const regular = ["--herp", "derp"]; + const alt = ["--harp", "derp"]; + const opts = { + alias: { h: ["herp", "harp"] }, + boolean: "h" + }; + const aliasedArgv = parse(aliased, opts); + const propertyArgv = parse(regular, opts); + const altPropertyArgv = parse(alt, opts); + const expected = { + harp: true, + herp: true, + h: true, + _: ["derp"] + }; + assertEquals(aliasedArgv, expected); + assertEquals(propertyArgv, expected); + assertEquals(altPropertyArgv, expected); +}); + +test(function booleanAndAliasUsingExplicitTrue(): void { + const aliased = ["-h", "true"]; + const regular = ["--herp", "true"]; + const opts = { + alias: { h: "herp" }, + boolean: "h" + }; + const aliasedArgv = parse(aliased, opts); + const propertyArgv = parse(regular, opts); + const expected = { + herp: true, + h: true, + _: [] + }; + + assertEquals(aliasedArgv, expected); + assertEquals(propertyArgv, expected); +}); + +// regression, see https://github.com/substack/node-optimist/issues/71 +// boolean and --x=true +test(function booleanAndNonBoolean(): void { + const parsed = parse(["--boool", "--other=true"], { + boolean: "boool" + }); + + assertEquals(parsed.boool, true); + assertEquals(parsed.other, "true"); + + const parsed2 = parse(["--boool", "--other=false"], { + boolean: "boool" + }); + + assertEquals(parsed2.boool, true); + assertEquals(parsed2.other, "false"); +}); + +test(function booleanParsingTrue(): void { + const parsed = parse(["--boool=true"], { + default: { + boool: false + }, + boolean: ["boool"] + }); + + assertEquals(parsed.boool, true); +}); + +test(function booleanParsingFalse(): void { + const parsed = parse(["--boool=false"], { + default: { + boool: true + }, + boolean: ["boool"] + }); + + assertEquals(parsed.boool, false); +}); + +test(function booleanParsingTrueLike(): void { + const parsed = parse(["-t", "true123"], { boolean: ["t"] }); + assertEquals(parsed.t, true); + + const parsed2 = parse(["-t", "123"], { boolean: ["t"] }); + assertEquals(parsed2.t, true); + + const parsed3 = parse(["-t", "false123"], { boolean: ["t"] }); + assertEquals(parsed3.t, true); +}); diff --git a/std/flags/dash_test.ts b/std/flags/dash_test.ts new file mode 100755 index 000000000..f4901b352 --- /dev/null +++ b/std/flags/dash_test.ts @@ -0,0 +1,29 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; +import { parse } from "./mod.ts"; + +test(function hyphen(): void { + assertEquals(parse(["-n", "-"]), { n: "-", _: [] }); + assertEquals(parse(["-"]), { _: ["-"] }); + assertEquals(parse(["-f-"]), { f: "-", _: [] }); + assertEquals(parse(["-b", "-"], { boolean: "b" }), { b: true, _: ["-"] }); + assertEquals(parse(["-s", "-"], { string: "s" }), { s: "-", _: [] }); +}); + +test(function doubleDash(): void { + assertEquals(parse(["-a", "--", "b"]), { a: true, _: ["b"] }); + assertEquals(parse(["--a", "--", "b"]), { a: true, _: ["b"] }); + assertEquals(parse(["--a", "--", "b"]), { a: true, _: ["b"] }); +}); + +test(function moveArgsAfterDoubleDashIntoOwnArray(): void { + assertEquals( + parse(["--name", "John", "before", "--", "after"], { "--": true }), + { + name: "John", + _: ["before"], + "--": ["after"] + } + ); +}); diff --git a/std/flags/default_bool_test.ts b/std/flags/default_bool_test.ts new file mode 100755 index 000000000..dc953cdc8 --- /dev/null +++ b/std/flags/default_bool_test.ts @@ -0,0 +1,33 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; +import { parse } from "./mod.ts"; + +test(function booleanDefaultTrue(): void { + const argv = parse([], { + boolean: "sometrue", + default: { sometrue: true } + }); + assertEquals(argv.sometrue, true); +}); + +test(function booleanDefaultFalse(): void { + const argv = parse([], { + boolean: "somefalse", + default: { somefalse: false } + }); + assertEquals(argv.somefalse, false); +}); + +test(function booleanDefaultNull(): void { + const argv = parse([], { + boolean: "maybe", + default: { maybe: null } + }); + assertEquals(argv.maybe, null); + const argv2 = parse(["--maybe"], { + boolean: "maybe", + default: { maybe: null } + }); + assertEquals(argv2.maybe, true); +}); diff --git a/std/flags/dotted_test.ts b/std/flags/dotted_test.ts new file mode 100755 index 000000000..3ec7409e7 --- /dev/null +++ b/std/flags/dotted_test.ts @@ -0,0 +1,24 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; +import { parse } from "./mod.ts"; + +test(function dottedAlias(): void { + const argv = parse(["--a.b", "22"], { + default: { "a.b": 11 }, + alias: { "a.b": "aa.bb" } + }); + assertEquals(argv.a.b, 22); + assertEquals(argv.aa.bb, 22); +}); + +test(function dottedDefault(): void { + const argv = parse([], { default: { "a.b": 11 }, alias: { "a.b": "aa.bb" } }); + assertEquals(argv.a.b, 11); + assertEquals(argv.aa.bb, 11); +}); + +test(function dottedDefaultWithNoAlias(): void { + const argv = parse([], { default: { "a.b": 11 } }); + assertEquals(argv.a.b, 11); +}); diff --git a/std/flags/example.ts b/std/flags/example.ts new file mode 100644 index 000000000..04ace4673 --- /dev/null +++ b/std/flags/example.ts @@ -0,0 +1,5 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +const { args } = Deno; +import { parse } from "./mod.ts"; + +console.dir(parse(args)); diff --git a/std/flags/kv_short_test.ts b/std/flags/kv_short_test.ts new file mode 100755 index 000000000..271e5c67d --- /dev/null +++ b/std/flags/kv_short_test.ts @@ -0,0 +1,14 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; +import { parse } from "./mod.ts"; + +test(function short(): void { + const argv = parse(["-b=123"]); + assertEquals(argv, { b: 123, _: [] }); +}); + +test(function multiShort(): void { + const argv = parse(["-a=whatever", "-b=robots"]); + assertEquals(argv, { a: "whatever", b: "robots", _: [] }); +}); diff --git a/std/flags/long_test.ts b/std/flags/long_test.ts new file mode 100755 index 000000000..5b14fc630 --- /dev/null +++ b/std/flags/long_test.ts @@ -0,0 +1,20 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; +import { parse } from "./mod.ts"; + +test(function longOpts(): void { + assertEquals(parse(["--bool"]), { bool: true, _: [] }); + assertEquals(parse(["--pow", "xixxle"]), { pow: "xixxle", _: [] }); + assertEquals(parse(["--pow=xixxle"]), { pow: "xixxle", _: [] }); + assertEquals(parse(["--host", "localhost", "--port", "555"]), { + host: "localhost", + port: 555, + _: [] + }); + assertEquals(parse(["--host=localhost", "--port=555"]), { + host: "localhost", + port: 555, + _: [] + }); +}); diff --git a/std/flags/mod.ts b/std/flags/mod.ts new file mode 100644 index 000000000..952315319 --- /dev/null +++ b/std/flags/mod.ts @@ -0,0 +1,321 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +export interface ArgParsingOptions { + unknown?: (i: unknown) => unknown; + boolean?: boolean | string | string[]; + alias?: { [key: string]: string | string[] }; + string?: string | string[]; + default?: { [key: string]: unknown }; + "--"?: boolean; + stopEarly?: boolean; +} + +const DEFAULT_OPTIONS = { + unknown: (i: unknown): unknown => i, + boolean: false, + alias: {}, + string: [], + default: {}, + "--": false, + stopEarly: false +}; + +interface Flags { + bools: { [key: string]: boolean }; + strings: { [key: string]: boolean }; + unknownFn: (i: unknown) => unknown; + allBools: boolean; +} + +interface NestedMapping { + [key: string]: NestedMapping | unknown; +} + +function get<T>(obj: { [s: string]: T }, key: string): T | undefined { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + return obj[key]; + } +} + +function isNumber(x: unknown): boolean { + if (typeof x === "number") return true; + if (/^0x[0-9a-f]+$/i.test(String(x))) return true; + return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(String(x)); +} + +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; + }); + + const key = keys[keys.length - 1]; + return key in o; +} + +export function parse( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + args: any[], + initialOptions?: ArgParsingOptions + // eslint-disable-next-line @typescript-eslint/no-explicit-any +): { [key: string]: any } { + const options: ArgParsingOptions = { + ...DEFAULT_OPTIONS, + ...(initialOptions || {}) + }; + + const flags: Flags = { + bools: {}, + strings: {}, + unknownFn: options.unknown!, + allBools: false + }; + + if (options.boolean !== undefined) { + if (typeof options.boolean === "boolean") { + flags.allBools = !!options.boolean; + } else { + const booleanArgs: string[] = + typeof options.boolean === "string" + ? [options.boolean] + : options.boolean; + + booleanArgs.filter(Boolean).forEach( + (key: string): void => { + flags.bools[key] = true; + } + ); + } + } + + const aliases: { [key: string]: string[] } = {}; + if (options.alias !== undefined) { + for (const key in options.alias) { + const val = get(options.alias, key)!; + + if (typeof val === "string") { + aliases[key] = [val]; + } else { + aliases[key] = val; + } + + for (const alias of get(aliases, key)!) { + aliases[alias] = [key].concat( + aliases[key].filter((y: string): boolean => alias !== y) + ); + } + } + } + + if (options.string !== undefined) { + const stringArgs = + typeof options.string === "string" ? [options.string] : options.string; + + stringArgs.filter(Boolean).forEach(function(key): void { + flags.strings[key] = true; + const alias = get(aliases, key); + if (alias) { + alias.forEach( + (alias: string): void => { + flags.strings[alias] = true; + } + ); + } + }); + } + + const defaults = options.default!; + + const argv: { [key: string]: unknown[] } = { _: [] }; + + function argDefined(key: string, arg: string): boolean { + return ( + (flags.allBools && /^--[^=]+$/.test(arg)) || + get(flags.bools, key) || + !!get(flags.strings, key) || + !!get(aliases, key) + ); + } + + function setKey(obj: NestedMapping, keys: string[], value: unknown): void { + let o = obj; + keys.slice(0, -1).forEach(function(key): void { + if (get(o, key) === undefined) { + o[key] = {}; + } + o = get(o, key) as NestedMapping; + }); + + const key = keys[keys.length - 1]; + if ( + get(o, key) === undefined || + get(flags.bools, key) || + typeof get(o, key) === "boolean" + ) { + o[key] = value; + } else if (Array.isArray(get(o, key))) { + (o[key] as unknown[]).push(value); + } else { + o[key] = [get(o, key), value]; + } + } + + function setArg( + key: string, + val: unknown, + arg: string | undefined = undefined + ): void { + if (arg && flags.unknownFn && !argDefined(key, arg)) { + if (flags.unknownFn(arg) === false) return; + } + + 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); + }); + } + + function aliasIsBoolean(key: string): boolean { + return get(aliases, key)!.some(function(x): boolean { + return get(flags.bools, x)!; + }); + } + + Object.keys(flags.bools).forEach(function(key): void { + setArg(key, defaults[key] === undefined ? false : defaults[key]); + }); + + let notFlags: string[] = []; + + // all args after "--" are not parsed + if (args.indexOf("--") !== -1) { + notFlags = args.slice(args.indexOf("--") + 1); + args = args.slice(0, args.indexOf("--")); + } + + for (let i = 0; i < args.length; i++) { + 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 key = m[1]; + const value = m[2]; + + if (flags.bools[key]) { + const booleanValue = value !== "false"; + setArg(key, booleanValue, arg); + } else { + setArg(key, value, arg); + } + } else if (/^--no-.+/.test(arg)) { + const key = arg.match(/^--no-(.+)/)![1]; + setArg(key, false, arg); + } else if (/^--.+/.test(arg)) { + const key = arg.match(/^--(.+)/)![1]; + const next = args[i + 1]; + if ( + next !== undefined && + !/^-/.test(next) && + !get(flags.bools, key) && + !flags.allBools && + (get(aliases, key) ? !aliasIsBoolean(key) : true) + ) { + setArg(key, next, arg); + i++; + } else if (/^(true|false)$/.test(next)) { + setArg(key, next === "true", arg); + i++; + } else { + setArg(key, get(flags.strings, key) ? "" : true, arg); + } + } else if (/^-[^-]+/.test(arg)) { + const letters = arg.slice(1, -1).split(""); + + let broken = false; + for (let j = 0; j < letters.length; j++) { + const next = arg.slice(j + 2); + + if (next === "-") { + setArg(letters[j], next, arg); + continue; + } + + if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) { + setArg(letters[j], next.split("=")[1], arg); + broken = true; + break; + } + + if ( + /[A-Za-z]/.test(letters[j]) && + /-?\d+(\.\d*)?(e-?\d+)?$/.test(next) + ) { + setArg(letters[j], next, arg); + broken = true; + break; + } + + if (letters[j + 1] && letters[j + 1].match(/\W/)) { + setArg(letters[j], arg.slice(j + 2), arg); + broken = true; + break; + } else { + setArg(letters[j], get(flags.strings, letters[j]) ? "" : true, arg); + } + } + + const key = arg.slice(-1)[0]; + if (!broken && key !== "-") { + if ( + args[i + 1] && + !/^(-|--)[^-]/.test(args[i + 1]) && + !get(flags.bools, key) && + (get(aliases, key) ? !aliasIsBoolean(key) : true) + ) { + setArg(key, args[i + 1], arg); + i++; + } else if (args[i + 1] && /^(true|false)$/.test(args[i + 1])) { + setArg(key, args[i + 1] === "true", arg); + i++; + } else { + setArg(key, get(flags.strings, key) ? "" : true, arg); + } + } + } else { + if (!flags.unknownFn || flags.unknownFn(arg) !== false) { + argv._.push(flags.strings["_"] || !isNumber(arg) ? arg : Number(arg)); + } + if (options.stopEarly) { + argv._.push(...args.slice(i + 1)); + break; + } + } + } + + Object.keys(defaults).forEach(function(key): void { + if (!hasKey(argv, key.split("."))) { + setKey(argv, key.split("."), defaults[key]); + + (aliases[key] || []).forEach(function(x): void { + setKey(argv, x.split("."), defaults[key]); + }); + } + }); + + if (options["--"]) { + argv["--"] = []; + notFlags.forEach(function(key): void { + argv["--"].push(key); + }); + } else { + notFlags.forEach(function(key): void { + argv._.push(key); + }); + } + + return argv; +} diff --git a/std/flags/num_test.ts b/std/flags/num_test.ts new file mode 100755 index 000000000..1123f7ecc --- /dev/null +++ b/std/flags/num_test.ts @@ -0,0 +1,41 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; +import { parse } from "./mod.ts"; + +test(function nums(): void { + const argv = parse([ + "-x", + "1234", + "-y", + "5.67", + "-z", + "1e7", + "-w", + "10f", + "--hex", + "0xdeadbeef", + "789" + ]); + assertEquals(argv, { + x: 1234, + y: 5.67, + z: 1e7, + w: "10f", + hex: 0xdeadbeef, + _: [789] + }); + assertEquals(typeof argv.x, "number"); + assertEquals(typeof argv.y, "number"); + assertEquals(typeof argv.z, "number"); + assertEquals(typeof argv.w, "string"); + assertEquals(typeof argv.hex, "number"); + assertEquals(typeof argv._[0], "number"); +}); + +test(function alreadyNumber(): void { + const argv = parse(["-x", 1234, 789]); + assertEquals(argv, { x: 1234, _: [789] }); + assertEquals(typeof argv.x, "number"); + assertEquals(typeof argv._[0], "number"); +}); diff --git a/std/flags/parse_test.ts b/std/flags/parse_test.ts new file mode 100755 index 000000000..9918ce8bb --- /dev/null +++ b/std/flags/parse_test.ts @@ -0,0 +1,201 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; +import { parse } from "./mod.ts"; + +test(function _arseArgs(): void { + assertEquals(parse(["--no-moo"]), { moo: false, _: [] }); + assertEquals(parse(["-v", "a", "-v", "b", "-v", "c"]), { + v: ["a", "b", "c"], + _: [] + }); +}); + +test(function comprehensive(): void { + assertEquals( + parse([ + "--name=meowmers", + "bare", + "-cats", + "woo", + "-h", + "awesome", + "--multi=quux", + "--key", + "value", + "-b", + "--bool", + "--no-meep", + "--multi=baz", + "--", + "--not-a-flag", + "eek" + ]), + { + c: true, + a: true, + t: true, + s: "woo", + h: "awesome", + b: true, + bool: true, + key: "value", + multi: ["quux", "baz"], + meep: false, + name: "meowmers", + _: ["bare", "--not-a-flag", "eek"] + } + ); +}); + +test(function flagBoolean(): void { + const argv = parse(["-t", "moo"], { boolean: "t" }); + assertEquals(argv, { t: true, _: ["moo"] }); + assertEquals(typeof argv.t, "boolean"); +}); + +test(function flagBooleanValue(): void { + const argv = parse(["--verbose", "false", "moo", "-t", "true"], { + boolean: ["t", "verbose"], + default: { verbose: true } + }); + + assertEquals(argv, { + verbose: false, + t: true, + _: ["moo"] + }); + + assertEquals(typeof argv.verbose, "boolean"); + assertEquals(typeof argv.t, "boolean"); +}); + +test(function newlinesInParams(): void { + const args = parse(["-s", "X\nX"]); + assertEquals(args, { _: [], s: "X\nX" }); + + // reproduce in bash: + // VALUE="new + // line" + // deno program.js --s="$VALUE" + const args2 = parse(["--s=X\nX"]); + assertEquals(args2, { _: [], s: "X\nX" }); +}); + +test(function strings(): void { + const s = parse(["-s", "0001234"], { string: "s" }).s; + assertEquals(s, "0001234"); + assertEquals(typeof s, "string"); + + const x = parse(["-x", "56"], { string: "x" }).x; + assertEquals(x, "56"); + assertEquals(typeof x, "string"); +}); + +test(function stringArgs(): void { + const s = parse([" ", " "], { string: "_" })._; + assertEquals(s.length, 2); + assertEquals(typeof s[0], "string"); + assertEquals(s[0], " "); + assertEquals(typeof s[1], "string"); + assertEquals(s[1], " "); +}); + +test(function emptyStrings(): void { + const s = parse(["-s"], { string: "s" }).s; + assertEquals(s, ""); + assertEquals(typeof s, "string"); + + const str = parse(["--str"], { string: "str" }).str; + assertEquals(str, ""); + assertEquals(typeof str, "string"); + + const letters = parse(["-art"], { + string: ["a", "t"] + }); + + assertEquals(letters.a, ""); + assertEquals(letters.r, true); + assertEquals(letters.t, ""); +}); + +test(function stringAndAlias(): void { + const x = parse(["--str", "000123"], { + string: "s", + alias: { s: "str" } + }); + + assertEquals(x.str, "000123"); + assertEquals(typeof x.str, "string"); + assertEquals(x.s, "000123"); + assertEquals(typeof x.s, "string"); + + const y = parse(["-s", "000123"], { + string: "str", + alias: { str: "s" } + }); + + assertEquals(y.str, "000123"); + assertEquals(typeof y.str, "string"); + assertEquals(y.s, "000123"); + assertEquals(typeof y.s, "string"); +}); + +test(function slashBreak(): void { + assertEquals(parse(["-I/foo/bar/baz"]), { I: "/foo/bar/baz", _: [] }); + assertEquals(parse(["-xyz/foo/bar/baz"]), { + x: true, + y: true, + z: "/foo/bar/baz", + _: [] + }); +}); + +test(function alias(): void { + const argv = parse(["-f", "11", "--zoom", "55"], { + alias: { z: "zoom" } + }); + assertEquals(argv.zoom, 55); + assertEquals(argv.z, argv.zoom); + assertEquals(argv.f, 11); +}); + +test(function multiAlias(): void { + const argv = parse(["-f", "11", "--zoom", "55"], { + alias: { z: ["zm", "zoom"] } + }); + assertEquals(argv.zoom, 55); + assertEquals(argv.z, argv.zoom); + assertEquals(argv.z, argv.zm); + assertEquals(argv.f, 11); +}); + +test(function nestedDottedObjects(): void { + const argv = parse([ + "--foo.bar", + "3", + "--foo.baz", + "4", + "--foo.quux.quibble", + "5", + "--foo.quux.oO", + "--beep.boop" + ]); + + assertEquals(argv.foo, { + bar: 3, + baz: 4, + quux: { + quibble: 5, + oO: true + } + }); + assertEquals(argv.beep, { boop: true }); +}); + +test(function flagBuiltinProperty(): void { + const argv = parse(["--toString", "--valueOf", "foo"]); + assertEquals(argv, { toString: true, valueOf: "foo", _: [] }); + assertEquals(typeof argv.toString, "boolean"); + assertEquals(typeof argv.valueOf, "string"); +}); diff --git a/std/flags/short_test.ts b/std/flags/short_test.ts new file mode 100755 index 000000000..f624381b1 --- /dev/null +++ b/std/flags/short_test.ts @@ -0,0 +1,46 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; +import { parse } from "./mod.ts"; + +test(function numbericShortArgs(): void { + assertEquals(parse(["-n123"]), { n: 123, _: [] }); + assertEquals(parse(["-123", "456"]), { 1: true, 2: true, 3: 456, _: [] }); +}); + +test(function short(): void { + assertEquals(parse(["-b"]), { b: true, _: [] }); + assertEquals(parse(["foo", "bar", "baz"]), { _: ["foo", "bar", "baz"] }); + assertEquals(parse(["-cats"]), { c: true, a: true, t: true, s: true, _: [] }); + assertEquals(parse(["-cats", "meow"]), { + c: true, + a: true, + t: true, + s: "meow", + _: [] + }); + assertEquals(parse(["-h", "localhost"]), { h: "localhost", _: [] }); + assertEquals(parse(["-h", "localhost", "-p", "555"]), { + h: "localhost", + p: 555, + _: [] + }); +}); + +test(function mixedShortBoolAndCapture(): void { + assertEquals(parse(["-h", "localhost", "-fp", "555", "script.js"]), { + f: true, + p: 555, + h: "localhost", + _: ["script.js"] + }); +}); + +test(function shortAndLong(): void { + assertEquals(parse(["-h", "localhost", "-fp", "555", "script.js"]), { + f: true, + p: 555, + h: "localhost", + _: ["script.js"] + }); +}); diff --git a/std/flags/stop_early_test.ts b/std/flags/stop_early_test.ts new file mode 100755 index 000000000..144a2921f --- /dev/null +++ b/std/flags/stop_early_test.ts @@ -0,0 +1,16 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; +import { parse } from "./mod.ts"; + +// stops parsing on the first non-option when stopEarly is set +test(function stopParsing(): void { + const argv = parse(["--aaa", "bbb", "ccc", "--ddd"], { + stopEarly: true + }); + + assertEquals(argv, { + aaa: "bbb", + _: ["ccc", "--ddd"] + }); +}); diff --git a/std/flags/unknown_test.ts b/std/flags/unknown_test.ts new file mode 100755 index 000000000..dde725a75 --- /dev/null +++ b/std/flags/unknown_test.ts @@ -0,0 +1,98 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; +import { parse } from "./mod.ts"; + +test(function booleanAndAliasIsNotUnknown(): void { + const unknown: unknown[] = []; + function unknownFn(arg: unknown): boolean { + unknown.push(arg); + return false; + } + const aliased = ["-h", "true", "--derp", "true"]; + const regular = ["--herp", "true", "-d", "true"]; + const opts = { + alias: { h: "herp" }, + boolean: "h", + unknown: unknownFn + }; + parse(aliased, opts); + parse(regular, opts); + + assertEquals(unknown, ["--derp", "-d"]); +}); + +test(function flagBooleanTrueAnyDoubleHyphenArgumentIsNotUnknown(): void { + const unknown: unknown[] = []; + function unknownFn(arg: unknown): boolean { + unknown.push(arg); + return false; + } + const argv = parse(["--honk", "--tacos=good", "cow", "-p", "55"], { + boolean: true, + unknown: unknownFn + }); + assertEquals(unknown, ["--tacos=good", "cow", "-p"]); + assertEquals(argv, { + honk: true, + _: [] + }); +}); + +test(function stringAndAliasIsNotUnkown(): void { + const unknown: unknown[] = []; + function unknownFn(arg: unknown): boolean { + unknown.push(arg); + return false; + } + const aliased = ["-h", "hello", "--derp", "goodbye"]; + const regular = ["--herp", "hello", "-d", "moon"]; + const opts = { + alias: { h: "herp" }, + string: "h", + unknown: unknownFn + }; + parse(aliased, opts); + parse(regular, opts); + + assertEquals(unknown, ["--derp", "-d"]); +}); + +test(function defaultAndAliasIsNotUnknown(): void { + const unknown: unknown[] = []; + function unknownFn(arg: unknown): boolean { + unknown.push(arg); + return false; + } + const aliased = ["-h", "hello"]; + const regular = ["--herp", "hello"]; + const opts = { + default: { h: "bar" }, + alias: { h: "herp" }, + unknown: unknownFn + }; + parse(aliased, opts); + parse(regular, opts); + + assertEquals(unknown, []); +}); + +test(function valueFollowingDoubleHyphenIsNotUnknown(): void { + const unknown: unknown[] = []; + function unknownFn(arg: unknown): boolean { + unknown.push(arg); + return false; + } + const aliased = ["--bad", "--", "good", "arg"]; + const opts = { + "--": true, + unknown: unknownFn + }; + const argv = parse(aliased, opts); + + assertEquals(unknown, ["--bad"]); + assertEquals(argv, { + "--": ["good", "arg"], + _: [] + }); +}); diff --git a/std/flags/whitespace_test.ts b/std/flags/whitespace_test.ts new file mode 100755 index 000000000..9e6ba7115 --- /dev/null +++ b/std/flags/whitespace_test.ts @@ -0,0 +1,8 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { test } from "../testing/mod.ts"; +import { assertEquals } from "../testing/asserts.ts"; +import { parse } from "./mod.ts"; + +test(function whitespaceShouldBeWhitespace(): void { + assertEquals(parse(["-x", "\t"]).x, "\t"); +}); |