summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md1
-rw-r--r--flags/README.md59
-rw-r--r--flags/example.ts4
-rw-r--r--flags/index.ts271
-rw-r--r--flags/test.ts13
-rwxr-xr-xflags/tests/all_bool.ts32
-rwxr-xr-xflags/tests/bool.ts158
-rwxr-xr-xflags/tests/dash.ts28
-rwxr-xr-xflags/tests/default_bool.ts32
-rwxr-xr-xflags/tests/dotted.ts19
-rwxr-xr-xflags/tests/kv_short.ts12
-rwxr-xr-xflags/tests/long.ts25
-rwxr-xr-xflags/tests/num.ts34
-rw-r--r--flags/tests/parse.ts182
-rwxr-xr-xflags/tests/short.ts57
-rwxr-xr-xflags/tests/stop_early.ts14
-rwxr-xr-xflags/tests/unknown.ts96
-rwxr-xr-xflags/tests/whitespace.ts6
-rwxr-xr-xtest.ts3
19 files changed, 1046 insertions, 0 deletions
diff --git a/README.md b/README.md
index 73d48ab3a..d3da80550 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,7 @@ for Deno.
| [colors](./colors/README.md) | Modules that generate ANSI color codes for the console. |
| [net](./net/README.md) | A framework for creating HTTP/HTTPS servers inspired by GoLang. |
| [path](./path/README.md) | A path manipulation library. |
+| [flags](./flags/README.md) | Command line arguments parser based on minimist. |
---
diff --git a/flags/README.md b/flags/README.md
new file mode 100644
index 000000000..baa9c19c4
--- /dev/null
+++ b/flags/README.md
@@ -0,0 +1,59 @@
+# flags
+
+Command line arguments parser for Deno based on minimist
+
+# Example
+
+``` ts
+import { args } from "deno";
+import parseArgs from "https://deno.land/x/parseargs/index.ts";
+
+console.dir(parseArgs(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 = parseArgs(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:
+* `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/flags/example.ts b/flags/example.ts
new file mode 100644
index 000000000..99d7102e2
--- /dev/null
+++ b/flags/example.ts
@@ -0,0 +1,4 @@
+import { args } from "deno";
+import parseArgs from "./index.ts";
+
+console.dir(parseArgs(args));
diff --git a/flags/index.ts b/flags/index.ts
new file mode 100644
index 000000000..17c41f69c
--- /dev/null
+++ b/flags/index.ts
@@ -0,0 +1,271 @@
+export interface ArgParsingOptions {
+ unknown?: Function;
+ boolean?: Boolean | string | string[];
+ alias?: { [key: string]: string | string[] };
+ string?: string | string[];
+ default?: { [key: string]: any };
+ "--"?: Boolean;
+ stopEarly?: Boolean;
+}
+
+const DEFAULT_OPTIONS = {
+ unknown: i => i,
+ boolean: false,
+ alias: {},
+ string: [],
+ default: {},
+ "--": false,
+ stopEarly: false
+};
+
+export default function parseArgs(
+ args,
+ initialOptions?: ArgParsingOptions
+): { [key: string]: any } {
+ const options: ArgParsingOptions = {
+ ...DEFAULT_OPTIONS,
+ ...(initialOptions || {})
+ };
+
+ const flags = {
+ bools: {},
+ strings: {},
+ unknownFn: options.unknown!,
+ allBools: false
+ };
+
+ // TODO: get rid of this, providing two different options
+ if (typeof options["boolean"] === "boolean" && options["boolean"]) {
+ flags.allBools = true;
+ } else {
+ []
+ .concat(options["boolean"])
+ .filter(Boolean)
+ .forEach(function(key) {
+ flags.bools[key] = true;
+ });
+ }
+
+ const aliases = {};
+ Object.keys(options.alias).forEach(function(key) {
+ aliases[key] = [].concat(options.alias[key]);
+ aliases[key].forEach(function(x) {
+ aliases[x] = [key].concat(
+ aliases[key].filter(function(y) {
+ return x !== y;
+ })
+ );
+ });
+ });
+
+ []
+ .concat(options.string)
+ .filter(Boolean)
+ .forEach(function(key) {
+ flags.strings[key] = true;
+ if (aliases[key]) {
+ flags.strings[aliases[key]] = true;
+ }
+ });
+
+ const defaults = options.default!;
+
+ const argv = { _: [] };
+ Object.keys(flags.bools).forEach(function(key) {
+ setArg(key, defaults[key] === undefined ? false : defaults[key]);
+ });
+
+ let notFlags = [];
+
+ if (args.indexOf("--") !== -1) {
+ notFlags = args.slice(args.indexOf("--") + 1);
+ args = args.slice(0, args.indexOf("--"));
+ }
+
+ function argDefined(key, arg) {
+ return (
+ (flags.allBools && /^--[^=]+$/.test(arg)) ||
+ flags.strings[key] ||
+ flags.bools[key] ||
+ aliases[key]
+ );
+ }
+
+ function setArg(key, val, arg = null): void {
+ if (arg && flags.unknownFn && !argDefined(key, arg)) {
+ if (flags.unknownFn(arg) === false) return;
+ }
+
+ const value = !flags.strings[key] && isNumber(val) ? Number(val) : val;
+ setKey(argv, key.split("."), value);
+
+ (aliases[key] || []).forEach(function(x) {
+ setKey(argv, x.split("."), value);
+ });
+ }
+
+ function setKey(obj, keys, value): void {
+ let o = obj;
+ keys.slice(0, -1).forEach(function(key) {
+ if (o[key] === undefined) o[key] = {};
+ o = o[key];
+ });
+
+ const key = keys[keys.length - 1];
+ if (
+ o[key] === undefined ||
+ flags.bools[key] ||
+ typeof o[key] === "boolean"
+ ) {
+ o[key] = value;
+ } else if (Array.isArray(o[key])) {
+ o[key].push(value);
+ } else {
+ o[key] = [o[key], value];
+ }
+ }
+
+ function aliasIsBoolean(key): boolean {
+ return aliases[key].some(function(x) {
+ return flags.bools[x];
+ });
+ }
+
+ 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];
+ let value = m[2];
+ if (flags.bools[key]) {
+ value = value !== "false";
+ }
+ 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) &&
+ !flags.bools[key] &&
+ !flags.allBools &&
+ (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, 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], flags.strings[letters[j]] ? "" : true, arg);
+ }
+ }
+
+ const key = arg.slice(-1)[0];
+ if (!broken && key !== "-") {
+ if (
+ args[i + 1] &&
+ !/^(-|--)[^-]/.test(args[i + 1]) &&
+ !flags.bools[key] &&
+ (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, 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.apply(argv._, args.slice(i + 1));
+ break;
+ }
+ }
+ }
+
+ Object.keys(defaults).forEach(function(key) {
+ if (!hasKey(argv, key.split("."))) {
+ setKey(argv, key.split("."), defaults[key]);
+
+ (aliases[key] || []).forEach(function(x) {
+ setKey(argv, x.split("."), defaults[key]);
+ });
+ }
+ });
+
+ if (options["--"]) {
+ argv["--"] = new Array();
+ notFlags.forEach(function(key) {
+ argv["--"].push(key);
+ });
+ } else {
+ notFlags.forEach(function(key) {
+ argv._.push(key);
+ });
+ }
+
+ return argv;
+}
+
+function hasKey(obj, keys) {
+ let o = obj;
+ keys.slice(0, -1).forEach(function(key) {
+ o = o[key] || {};
+ });
+
+ const key = keys[keys.length - 1];
+ return key in o;
+}
+
+function isNumber(x: any): boolean {
+ if (typeof x === "number") return true;
+ if (/^0x[0-9a-f]+$/i.test(x)) return true;
+ return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
+}
diff --git a/flags/test.ts b/flags/test.ts
new file mode 100644
index 000000000..fe4ec2c9f
--- /dev/null
+++ b/flags/test.ts
@@ -0,0 +1,13 @@
+import "./tests/all_bool.ts";
+import "./tests/bool.ts";
+import "./tests/dash.ts";
+import "./tests/default_bool.ts";
+import "./tests/dotted.ts";
+import "./tests/kv_short.ts";
+import "./tests/long.ts";
+import "./tests/num.ts";
+import "./tests/parse.ts";
+import "./tests/short.ts";
+import "./tests/stop_early.ts";
+import "./tests/unknown.ts";
+import "./tests/whitespace.ts";
diff --git a/flags/tests/all_bool.ts b/flags/tests/all_bool.ts
new file mode 100755
index 000000000..de696dda6
--- /dev/null
+++ b/flags/tests/all_bool.ts
@@ -0,0 +1,32 @@
+import { test, assertEqual } from "https://deno.land/x/testing/testing.ts";
+import parseArgs from "../index.ts";
+
+// flag boolean true (default all --args to boolean)
+test(function flagBooleanTrue() {
+ const argv = parseArgs(['moo', '--honk', 'cow'], {
+ boolean: true
+ });
+
+ assertEqual(argv, {
+ honk: true,
+ _: ['moo', 'cow']
+ });
+
+ assertEqual(typeof argv.honk, 'boolean');
+});
+
+// flag boolean true only affects double hyphen arguments without equals signs
+test(function flagBooleanTrueOnlyAffectsDoubleDash() {
+ var argv = parseArgs(['moo', '--honk', 'cow', '-p', '55', '--tacos=good'], {
+ boolean: true
+ });
+
+ assertEqual(argv, {
+ honk: true,
+ tacos: 'good',
+ p: 55,
+ _: ['moo', 'cow']
+ });
+
+ assertEqual(typeof argv.honk, 'boolean');
+});
diff --git a/flags/tests/bool.ts b/flags/tests/bool.ts
new file mode 100755
index 000000000..b2b96dfed
--- /dev/null
+++ b/flags/tests/bool.ts
@@ -0,0 +1,158 @@
+import { test, assertEqual } from "https://deno.land/x/testing/testing.ts";
+import parseArgs from "../index.ts";
+
+test(function flagBooleanDefaultFalse() {
+ const argv = parseArgs(['moo'], {
+ boolean: ['t', 'verbose'],
+ default: { verbose: false, t: false }
+ });
+
+ assertEqual(argv, {
+ verbose: false,
+ t: false,
+ _: ['moo']
+ });
+
+ assertEqual(typeof argv.verbose, 'boolean');
+ assertEqual(typeof argv.t, 'boolean');
+});
+
+test(function booleanGroups() {
+ const argv = parseArgs([ '-x', '-z', 'one', 'two', 'three' ], {
+ boolean: ['x','y','z']
+ });
+
+ assertEqual(argv, {
+ x : true,
+ y : false,
+ z : true,
+ _ : [ 'one', 'two', 'three' ]
+ });
+
+ assertEqual(typeof argv.x, 'boolean');
+ assertEqual(typeof argv.y, 'boolean');
+ assertEqual(typeof argv.z, 'boolean');
+});
+
+test(function booleanAndAliasWithChainableApi() {
+ const aliased = [ '-h', 'derp' ];
+ const regular = [ '--herp', 'derp' ];
+ const opts = {
+ herp: { alias: 'h', boolean: true }
+ };
+ const aliasedArgv = parseArgs(aliased, {
+ boolean: 'herp',
+ alias: { h: 'herp' }
+ });
+ const propertyArgv = parseArgs(regular, {
+ boolean: 'herp',
+ alias: { h: 'herp' }
+ });
+ const expected = {
+ herp: true,
+ h: true,
+ '_': [ 'derp' ]
+ };
+
+ assertEqual(aliasedArgv, expected);
+ assertEqual(propertyArgv, expected);
+});
+
+test(function booleanAndAliasWithOptionsHash() {
+ const aliased = [ '-h', 'derp' ];
+ const regular = [ '--herp', 'derp' ];
+ const opts = {
+ alias: { 'h': 'herp' },
+ boolean: 'herp'
+ };
+ const aliasedArgv = parseArgs(aliased, opts);
+ const propertyArgv = parseArgs(regular, opts);
+ const expected = {
+ herp: true,
+ h: true,
+ '_': [ 'derp' ]
+ };
+ assertEqual(aliasedArgv, expected);
+ assertEqual(propertyArgv, expected);
+});
+
+test(function booleanAndAliasArrayWithOptionsHash() {
+ const aliased = [ '-h', 'derp' ];
+ const regular = [ '--herp', 'derp' ];
+ const alt = [ '--harp', 'derp' ];
+ const opts = {
+ alias: { 'h': ['herp', 'harp'] },
+ boolean: 'h'
+ };
+ const aliasedArgv = parseArgs(aliased, opts);
+ const propertyArgv = parseArgs(regular, opts);
+ const altPropertyArgv = parseArgs(alt, opts);
+ const expected = {
+ harp: true,
+ herp: true,
+ h: true,
+ '_': [ 'derp' ]
+ };
+ assertEqual(aliasedArgv, expected);
+ assertEqual(propertyArgv, expected);
+ assertEqual(altPropertyArgv, expected);
+});
+
+test(function booleanAndAliasUsingExplicitTrue() {
+ const aliased = [ '-h', 'true' ];
+ const regular = [ '--herp', 'true' ];
+ const opts = {
+ alias: { h: 'herp' },
+ boolean: 'h'
+ };
+ const aliasedArgv = parseArgs(aliased, opts);
+ const propertyArgv = parseArgs(regular, opts);
+ const expected = {
+ herp: true,
+ h: true,
+ '_': [ ]
+ };
+
+ assertEqual(aliasedArgv, expected);
+ assertEqual(propertyArgv, expected);
+});
+
+// regression, see https://github.com/substack/node-optimist/issues/71
+// boolean and --x=true
+test(function booleanAndNonBoolean() {
+ const parsed = parseArgs(['--boool', '--other=true'], {
+ boolean: 'boool'
+ });
+
+ assertEqual(parsed.boool, true);
+ assertEqual(parsed.other, 'true');
+
+ const parsed2 = parseArgs(['--boool', '--other=false'], {
+ boolean: 'boool'
+ });
+
+ assertEqual(parsed2.boool, true);
+ assertEqual(parsed2.other, 'false');
+});
+
+test(function booleanParsingTrue() {
+ const parsed = parseArgs(['--boool=true'], {
+ default: {
+ boool: false
+ },
+ boolean: ['boool']
+ });
+
+ assertEqual(parsed.boool, true);
+});
+
+test(function booleanParsingFalse() {
+ const parsed = parseArgs(['--boool=false'], {
+ default: {
+ boool: true
+ },
+ boolean: ['boool']
+ });
+
+ assertEqual(parsed.boool, false);
+});
diff --git a/flags/tests/dash.ts b/flags/tests/dash.ts
new file mode 100755
index 000000000..87b3ab480
--- /dev/null
+++ b/flags/tests/dash.ts
@@ -0,0 +1,28 @@
+import { test, assertEqual } from "https://deno.land/x/testing/testing.ts";
+import parseArgs from "../index.ts";
+
+test(function hyphen() {
+ assertEqual(parseArgs([ '-n', '-' ]), { n: '-', _: [] });
+ assertEqual(parseArgs([ '-' ]), { _: [ '-' ] });
+ assertEqual(parseArgs([ '-f-' ]), { f: '-', _: [] });
+ assertEqual(
+ parseArgs([ '-b', '-' ], { boolean: 'b' }),
+ { b: true, _: [ '-' ] }
+ );
+ assertEqual(
+ parseArgs([ '-s', '-' ], { string: 's' }),
+ { s: '-', _: [] }
+ );
+});
+
+test(function doubleDash() {
+ assertEqual(parseArgs([ '-a', '--', 'b' ]), { a: true, _: [ 'b' ] });
+ assertEqual(parseArgs([ '--a', '--', 'b' ]), { a: true, _: [ 'b' ] });
+ assertEqual(parseArgs([ '--a', '--', 'b' ]), { a: true, _: [ 'b' ] });
+});
+
+test(function moveArgsAfterDoubleDashIntoOwnArray() {
+ assertEqual(
+ parseArgs([ '--name', 'John', 'before', '--', 'after' ], { '--': true }),
+ { name: 'John', _: [ 'before' ], '--': [ 'after' ] });
+});
diff --git a/flags/tests/default_bool.ts b/flags/tests/default_bool.ts
new file mode 100755
index 000000000..92684ad7b
--- /dev/null
+++ b/flags/tests/default_bool.ts
@@ -0,0 +1,32 @@
+import { test, assertEqual } from "https://deno.land/x/testing/testing.ts";
+import parseArgs from "../index.ts";
+
+test(function booleanDefaultTrue() {
+ const argv = parseArgs([], {
+ boolean: 'sometrue',
+ default: { sometrue: true }
+ });
+ assertEqual(argv.sometrue, true);
+});
+
+test(function booleanDefaultFalse() {
+ const argv = parseArgs([], {
+ boolean: 'somefalse',
+ default: { somefalse: false }
+ });
+ assertEqual(argv.somefalse, false);
+});
+
+test(function booleanDefaultNull() {
+ const argv = parseArgs([], {
+ boolean: 'maybe',
+ default: { maybe: null }
+ });
+ assertEqual(argv.maybe, null);
+ const argv2 = parseArgs(['--maybe'], {
+ boolean: 'maybe',
+ default: { maybe: null }
+ });
+ assertEqual(argv2.maybe, true);
+
+})
diff --git a/flags/tests/dotted.ts b/flags/tests/dotted.ts
new file mode 100755
index 000000000..aea03dd7c
--- /dev/null
+++ b/flags/tests/dotted.ts
@@ -0,0 +1,19 @@
+import { test, assertEqual } from "https://deno.land/x/testing/testing.ts";
+import parseArgs from "../index.ts";
+
+test(function dottedAlias() {
+ const argv = parseArgs(['--a.b', '22'], {default: {'a.b': 11}, alias: {'a.b': 'aa.bb'}});
+ assertEqual(argv.a.b, 22);
+ assertEqual(argv.aa.bb, 22);
+});
+
+test(function dottedDefault() {
+ const argv = parseArgs('', {default: {'a.b': 11}, alias: {'a.b': 'aa.bb'}});
+ assertEqual(argv.a.b, 11);
+ assertEqual(argv.aa.bb, 11);
+});
+
+test(function dottedDefaultWithNoAlias() {
+ const argv = parseArgs('', {default: {'a.b': 11}});
+ assertEqual(argv.a.b, 11);
+});
diff --git a/flags/tests/kv_short.ts b/flags/tests/kv_short.ts
new file mode 100755
index 000000000..10b4154e0
--- /dev/null
+++ b/flags/tests/kv_short.ts
@@ -0,0 +1,12 @@
+import { test, assertEqual } from "https://deno.land/x/testing/testing.ts";
+import parseArgs from "../index.ts";
+
+test(function short() {
+ const argv = parseArgs([ '-b=123' ]);
+ assertEqual(argv, { b: 123, _: [] });
+});
+
+test(function multiShort() {
+ const argv = parseArgs([ '-a=whatever', '-b=robots' ]);
+ assertEqual(argv, { a: 'whatever', b: 'robots', _: [] });
+});
diff --git a/flags/tests/long.ts b/flags/tests/long.ts
new file mode 100755
index 000000000..876e6af2b
--- /dev/null
+++ b/flags/tests/long.ts
@@ -0,0 +1,25 @@
+import { test, assertEqual } from "https://deno.land/x/testing/testing.ts";
+import parseArgs from "../index.ts";
+
+test(function longOpts() {
+ assertEqual(
+ parseArgs([ '--bool' ]),
+ { bool : true, _ : [] },
+ );
+ assertEqual(
+ parseArgs([ '--pow', 'xixxle' ]),
+ { pow : 'xixxle', _ : [] },
+ );
+ assertEqual(
+ parseArgs([ '--pow=xixxle' ]),
+ { pow : 'xixxle', _ : [] },
+ );
+ assertEqual(
+ parseArgs([ '--host', 'localhost', '--port', '555' ]),
+ { host : 'localhost', port : 555, _ : [] },
+ );
+ assertEqual(
+ parseArgs([ '--host=localhost', '--port=555' ]),
+ { host : 'localhost', port : 555, _ : [] },
+ );
+});
diff --git a/flags/tests/num.ts b/flags/tests/num.ts
new file mode 100755
index 000000000..85efa76a6
--- /dev/null
+++ b/flags/tests/num.ts
@@ -0,0 +1,34 @@
+import { test, assertEqual } from "https://deno.land/x/testing/testing.ts";
+import parseArgs from "../index.ts";
+
+test(function nums() {
+ const argv = parseArgs([
+ '-x', '1234',
+ '-y', '5.67',
+ '-z', '1e7',
+ '-w', '10f',
+ '--hex', '0xdeadbeef',
+ '789'
+ ]);
+ assertEqual(argv, {
+ x : 1234,
+ y : 5.67,
+ z : 1e7,
+ w : '10f',
+ hex : 0xdeadbeef,
+ _ : [ 789 ]
+ });
+ assertEqual(typeof argv.x, 'number');
+ assertEqual(typeof argv.y, 'number');
+ assertEqual(typeof argv.z, 'number');
+ assertEqual(typeof argv.w, 'string');
+ assertEqual(typeof argv.hex, 'number');
+ assertEqual(typeof argv._[0], 'number');
+});
+
+test(function alreadyNumber() {
+ const argv = parseArgs([ '-x', 1234, 789 ]);
+ assertEqual(argv, { x : 1234, _ : [ 789 ] });
+ assertEqual(typeof argv.x, 'number');
+ assertEqual(typeof argv._[0], 'number');
+});
diff --git a/flags/tests/parse.ts b/flags/tests/parse.ts
new file mode 100644
index 000000000..3e85f58ef
--- /dev/null
+++ b/flags/tests/parse.ts
@@ -0,0 +1,182 @@
+import { test, assertEqual } from "https://deno.land/x/testing/testing.ts";
+import parseArgs from "../index.ts";
+
+
+test(function _arseArgs() {
+ assertEqual(
+ parseArgs([ '--no-moo' ]),
+ { moo : false, _ : [] },
+ );
+ assertEqual(
+ parseArgs([ '-v', 'a', '-v', 'b', '-v', 'c' ]),
+ { v : ['a','b','c'], _ : [] },
+ );
+});
+
+test(function comprehensive() {
+ assertEqual(
+ parseArgs([
+ '--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() {
+ const argv = parseArgs([ '-t', 'moo' ], { boolean: 't' });
+ assertEqual(argv, { t : true, _ : [ 'moo' ] });
+ assertEqual(typeof argv.t, 'boolean');
+});
+
+test(function flagBooleanValue() {
+ const argv = parseArgs(['--verbose', 'false', 'moo', '-t', 'true'], {
+ boolean: [ 't', 'verbose' ],
+ default: { verbose: true }
+ });
+
+ assertEqual(argv, {
+ verbose: false,
+ t: true,
+ _: ['moo']
+ });
+
+ assertEqual(typeof argv.verbose, 'boolean');
+ assertEqual(typeof argv.t, 'boolean');
+});
+
+test(function newlinesInParams() {
+ const args = parseArgs([ '-s', "X\nX" ])
+ assertEqual(args, { _ : [], s : "X\nX" });
+
+ // reproduce in bash:
+ // VALUE="new
+ // line"
+ // deno program.js --s="$VALUE"
+ const args2 = parseArgs([ "--s=X\nX" ])
+ assertEqual(args2, { _ : [], s : "X\nX" });
+});
+
+test(function strings() {
+ const s = parseArgs([ '-s', '0001234' ], { string: 's' }).s;
+ assertEqual(s, '0001234');
+ assertEqual(typeof s, 'string');
+
+ const x = parseArgs([ '-x', '56' ], { string: 'x' }).x;
+ assertEqual(x, '56');
+ assertEqual(typeof x, 'string');
+});
+
+test(function stringArgs() {
+ const s = parseArgs([ ' ', ' ' ], { string: '_' })._;
+ assertEqual(s.length, 2);
+ assertEqual(typeof s[0], 'string');
+ assertEqual(s[0], ' ');
+ assertEqual(typeof s[1], 'string');
+ assertEqual(s[1], ' ');
+});
+
+test(function emptyStrings() {
+ const s = parseArgs([ '-s' ], { string: 's' }).s;
+ assertEqual(s, '');
+ assertEqual(typeof s, 'string');
+
+ const str = parseArgs([ '--str' ], { string: 'str' }).str;
+ assertEqual(str, '');
+ assertEqual(typeof str, 'string');
+
+ const letters = parseArgs([ '-art' ], {
+ string: [ 'a', 't' ]
+ });
+
+ assertEqual(letters.a, '');
+ assertEqual(letters.r, true);
+ assertEqual(letters.t, '');
+});
+
+
+test(function stringAndAlias() {
+ const x = parseArgs([ '--str', '000123' ], {
+ string: 's',
+ alias: { s: 'str' }
+ });
+
+ assertEqual(x.str, '000123');
+ assertEqual(typeof x.str, 'string');
+ assertEqual(x.s, '000123');
+ assertEqual(typeof x.s, 'string');
+
+ const y = parseArgs([ '-s', '000123' ], {
+ string: 'str',
+ alias: { str: 's' }
+ });
+
+ assertEqual(y.str, '000123');
+ assertEqual(typeof y.str, 'string');
+ assertEqual(y.s, '000123');
+ assertEqual(typeof y.s, 'string');
+});
+
+test(function slashBreak() {
+ assertEqual(
+ parseArgs([ '-I/foo/bar/baz' ]),
+ { I : '/foo/bar/baz', _ : [] }
+ );
+ assertEqual(
+ parseArgs([ '-xyz/foo/bar/baz' ]),
+ { x : true, y : true, z : '/foo/bar/baz', _ : [] }
+ );
+});
+
+test(function alias() {
+ const argv = parseArgs([ '-f', '11', '--zoom', '55' ], {
+ alias: { z: 'zoom' }
+ });
+ assertEqual(argv.zoom, 55);
+ assertEqual(argv.z, argv.zoom);
+ assertEqual(argv.f, 11);
+});
+
+test(function multiAlias() {
+ const argv = parseArgs([ '-f', '11', '--zoom', '55' ], {
+ alias: { z: [ 'zm', 'zoom' ] }
+ });
+ assertEqual(argv.zoom, 55);
+ assertEqual(argv.z, argv.zoom);
+ assertEqual(argv.z, argv.zm);
+ assertEqual(argv.f, 11);
+});
+
+test(function nestedDottedObjects() {
+ const argv = parseArgs([
+ '--foo.bar', '3', '--foo.baz', '4',
+ '--foo.quux.quibble', '5', '--foo.quux.o_O',
+ '--beep.boop'
+ ]);
+
+ assertEqual(argv.foo, {
+ bar : 3,
+ baz : 4,
+ quux : {
+ quibble : 5,
+ o_O : true
+ }
+ });
+ assertEqual(argv.beep, { boop : true });
+}); \ No newline at end of file
diff --git a/flags/tests/short.ts b/flags/tests/short.ts
new file mode 100755
index 000000000..2253ac13d
--- /dev/null
+++ b/flags/tests/short.ts
@@ -0,0 +1,57 @@
+import { test, assertEqual } from "https://deno.land/x/testing/testing.ts";
+import parseArgs from "../index.ts";
+
+test(function numbericShortArgs() {
+ assertEqual(parseArgs([ '-n123' ]), { n: 123, _: [] });
+ assertEqual(
+ parseArgs([ '-123', '456' ]),
+ { 1: true, 2: true, 3: 456, _: [] }
+ );
+});
+
+test(function short() {
+ assertEqual(
+ parseArgs([ '-b' ]),
+ { b : true, _ : [] },
+ );
+ assertEqual(
+ parseArgs([ 'foo', 'bar', 'baz' ]),
+ { _ : [ 'foo', 'bar', 'baz' ] },
+ );
+ assertEqual(
+ parseArgs([ '-cats' ]),
+ { c : true, a : true, t : true, s : true, _ : [] },
+ );
+ assertEqual(
+ parseArgs([ '-cats', 'meow' ]),
+ { c : true, a : true, t : true, s : 'meow', _ : [] },
+ );
+ assertEqual(
+ parseArgs([ '-h', 'localhost' ]),
+ { h : 'localhost', _ : [] },
+ );
+ assertEqual(
+ parseArgs([ '-h', 'localhost', '-p', '555' ]),
+ { h : 'localhost', p : 555, _ : [] },
+ );
+});
+
+test(function mixedShortBoolAndCapture() {
+ assertEqual(
+ parseArgs([ '-h', 'localhost', '-fp', '555', 'script.js' ]),
+ {
+ f : true, p : 555, h : 'localhost',
+ _ : [ 'script.js' ]
+ }
+ );
+});
+
+test(function shortAndLong() {
+ assertEqual(
+ parseArgs([ '-h', 'localhost', '-fp', '555', 'script.js' ]),
+ {
+ f : true, p : 555, h : 'localhost',
+ _ : [ 'script.js' ]
+ }
+ );
+});
diff --git a/flags/tests/stop_early.ts b/flags/tests/stop_early.ts
new file mode 100755
index 000000000..2a62008b7
--- /dev/null
+++ b/flags/tests/stop_early.ts
@@ -0,0 +1,14 @@
+import { test, assertEqual } from "https://deno.land/x/testing/testing.ts";
+import parseArgs from "../index.ts";
+
+// stops parsing on the first non-option when stopEarly is set
+test(function stopParsing() {
+ const argv = parseArgs(['--aaa', 'bbb', 'ccc', '--ddd'], {
+ stopEarly: true
+ });
+
+ assertEqual(argv, {
+ aaa: 'bbb',
+ _: ['ccc', '--ddd']
+ });
+});
diff --git a/flags/tests/unknown.ts b/flags/tests/unknown.ts
new file mode 100755
index 000000000..d7c9db8d7
--- /dev/null
+++ b/flags/tests/unknown.ts
@@ -0,0 +1,96 @@
+import { test, assertEqual } from "https://deno.land/x/testing/testing.ts";
+import parseArgs from "../index.ts";
+
+test(function booleanAndAliasIsNotUnknown() {
+ const unknown = [];
+ function unknownFn(arg) {
+ 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
+ };
+ const aliasedArgv = parseArgs(aliased, opts);
+ const propertyArgv = parseArgs(regular, opts);
+
+ assertEqual(unknown, ['--derp', '-d']);
+});
+
+test(function flagBooleanTrueAnyDoubleHyphenArgumentIsNotUnknown() {
+ const unknown = [];
+ function unknownFn(arg) {
+ unknown.push(arg);
+ return false;
+ }
+ const argv = parseArgs(['--honk', '--tacos=good', 'cow', '-p', '55'], {
+ boolean: true,
+ unknown: unknownFn
+ });
+ assertEqual(unknown, ['--tacos=good', 'cow', '-p']);
+ assertEqual(argv, {
+ honk: true,
+ _: []
+ });
+});
+
+test(function stringAndAliasIsNotUnkown() {
+ const unknown = [];
+ function unknownFn(arg) {
+ 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
+ };
+ const aliasedArgv = parseArgs(aliased, opts);
+ const propertyArgv = parseArgs(regular, opts);
+
+ assertEqual(unknown, ['--derp', '-d']);
+});
+
+test(function defaultAndAliasIsNotUnknown() {
+ const unknown = [];
+ function unknownFn(arg) {
+ unknown.push(arg);
+ return false;
+ }
+ const aliased = [ '-h', 'hello' ];
+ const regular = [ '--herp', 'hello' ];
+ const opts = {
+ default: { 'h': 'bar' },
+ alias: { 'h': 'herp' },
+ unknown: unknownFn
+ };
+ const aliasedArgv = parseArgs(aliased, opts);
+ const propertyArgv = parseArgs(regular, opts);
+
+ assertEqual(unknown, []);
+});
+
+test(function valueFollowingDoubleHyphenIsNotUnknown() {
+ const unknown = [];
+ function unknownFn(arg) {
+ unknown.push(arg);
+ return false;
+ }
+ const aliased = [ '--bad', '--', 'good', 'arg' ];
+ const opts = {
+ '--': true,
+ unknown: unknownFn
+ };
+ const argv = parseArgs(aliased, opts);
+
+ assertEqual(unknown, ['--bad']);
+ assertEqual(argv, {
+ '--': ['good', 'arg'],
+ '_': []
+ })
+});
diff --git a/flags/tests/whitespace.ts b/flags/tests/whitespace.ts
new file mode 100755
index 000000000..1af0e77d2
--- /dev/null
+++ b/flags/tests/whitespace.ts
@@ -0,0 +1,6 @@
+import { test, assertEqual } from "https://deno.land/x/testing/testing.ts";
+import parseArgs from "../index.ts";
+
+test(function whitespaceShouldBeWhitespace() {
+ assertEqual(parseArgs([ '-x', '\t' ]).x, '\t');
+});
diff --git a/test.ts b/test.ts
index 1155202d1..f64e2ae95 100755
--- a/test.ts
+++ b/test.ts
@@ -4,6 +4,9 @@ import { run } from "deno";
// colors tests
import "colors/main_test.ts";
+// flags tests
+import "flags/test.ts";
+
// net tests
import "net/bufio_test.ts";
import "net/http_test.ts";