Skip to content

Commit

Permalink
fix: support using arg multiple times
Browse files Browse the repository at this point in the history
  • Loading branch information
ronhippler committed Apr 11, 2020
1 parent 15e8ac3 commit 2f79aae
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 138 deletions.
130 changes: 130 additions & 0 deletions cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import {
parse,
} from "./deps.ts";

export interface Args {
debug?: boolean;
fullscreen?: boolean;
help?: boolean;
quiet?: boolean;
interval?: number;
config?: string;
deno_args: string[];
extensions?: string[];
files: string[];
match?: string[];
runnerFlags: string[];
skip?: string[];
watch?: string[];
}

export function help() {
console.log(`
Usage:
denon [OPTIONS] [DENO_ARGS] [SCRIPT] [-- <SCRIPT_ARGS>]
OPTIONS:
-c, --config <file> A path to a config file, defaults to [default: .denonrc | .denonrc.json]
-d, --debug Debugging mode for more verbose logging
-e, --extensions List of extensions to look for separated by commas
-f, --fullscreen Clears the screen each reload
-h, --help Prints this
-i, --interval <ms> The number of milliseconds between each check
-m, --match <glob> Glob pattern for all the files to match
-q, --quiet Turns off all logging
-s, --skip <glob> Glob pattern for ignoring specific files or directories
-w, --watch List of paths to watch separated by commas
DENO_ARGS: Arguments passed to Deno to run SCRIPT (like permisssions)
`);
}

function chooseLast(arg: string | string[] | undefined): string | undefined {
if (Array.isArray(arg)) {
return arg[arg.length - 1];
}
return arg;
}

function convertToStringArray(
arg: string | string[] | undefined,
): string[] | undefined {
if (typeof arg === "string") {
return arg.split(",");
} else if (Array.isArray(arg)) {
return arg.reduce(
(acc, curr) => acc.concat(curr.split(",")),
[] as string[],
);
}
return arg;
}

export function parseArgs(args: string[]): Args {
if (args[0] === "--") {
args = args.slice(1);
}

let deno_args: string[] = [];
const files: string[] = [];

const flags = parse(args, {
string: ["config", "extensions", "interval", "match", "skip", "watch"],
boolean: ["debug", "fullscreen", "help", "quiet"],
alias: {
config: "c",
debug: "d",
extensions: "e",
fullscreen: "f",
help: "h",
interval: "i",
match: "m",
quiet: "q",
skip: "s",
watch: "w",
},
"--": true,
unknown: (arg: string, k?: string, v?: unknown) => {
if (k == null && v == null) {
files.push(arg);
return false;
}
deno_args.push(arg);
if (v && !arg.endsWith(String(v)) && typeof (v) !== "boolean") {
deno_args.push(String(v));
}
return false;
},
});

const script = deno_args[deno_args.length - 1];
if (script && !script.startsWith("-")) {
files.push(script);
deno_args = deno_args.slice(0, -1);
}

return {
debug: flags.debug,
help: flags.help,
fullscreen: flags.fullscreen,
quiet: flags.quiet,
config: chooseLast(flags.config),
interval: flags.interval ? parseInt(flags.interval, 10) : undefined,
extensions: convertToStringArray(flags.extensions),
match: convertToStringArray(flags.match),
skip: convertToStringArray(flags.skip),
watch: convertToStringArray(flags.watch),
files,
runnerFlags: flags["--"],
deno_args,
};
}

export function applyIfDefined(source: any, target: any, properties: string[]) {
properties.forEach((p) => {
const value = source[p];
if (value != null) {
target[p] = source[p];
}
});
}
48 changes: 42 additions & 6 deletions denon_test.ts → cli_test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { test, assertEquals } from "./test_deps.ts";

import { parseArgs, Args } from "./denon.ts";
import { parseArgs, Args, applyIfDefined } from "./cli.ts";

test(function parseArgsEmpty() {
const expected: Args = {
Expand Down Expand Up @@ -92,16 +92,52 @@ test(function parseArgsAll() {
config: "denon.json",
debug: true,
deno_args: ["--importmap=import_map.json", "-A"],
extensions: "js,ts",
extensions: ["js", "ts"],
files: ["mod.ts"],
fullscreen: true,
help: true,
interval: "500",
match: "foo/**",
interval: 500,
match: ["foo/**"],
quiet: true,
runnerFlags: ["--allow-net", "--port", "500"],
skip: "bar/**",
watch: "lib/**",
skip: ["bar/**"],
watch: ["lib/**"],
};
assertEquals(parseArgs(args), expected);
});

test(function parseSameArgsMultipleTimes() {
let args = "-dd -c .rc1 -c .rc2 -e js,ts -e py -m foo,bar -m bla".split(" ");
args = args.concat(
"-s foo,bar -s bla -w foo,bar -w bla file1 file2".split(" "),
);
const expected: Args = {
config: ".rc2",
debug: true,
deno_args: [],
extensions: ["js", "ts", "py"],
files: ["file1", "file2"],
fullscreen: false,
help: false,
interval: undefined,
match: ["foo", "bar", "bla"],
quiet: false,
runnerFlags: [],
skip: ["foo", "bar", "bla"],
watch: ["foo", "bar", "bla"],
};
assertEquals(parseArgs(args), expected);
});

test(function taetApplyIfDefined() {
const source = { a: 1, b: 2, d: undefined };
const target = { a: 3, c: 4 };
applyIfDefined(source, target, ["a"]);
assertEquals(target, { a: 1, c: 4 });
applyIfDefined(source, target, ["a", "b"]);
assertEquals(target, { a: 1, b: 2, c: 4 });
applyIfDefined(source, target, ["d"]);
assertEquals(target, { a: 1, b: 2, c: 4 });
applyIfDefined(source, target, ["e"]);
assertEquals(target, { a: 1, b: 2, c: 4 });
});
149 changes: 17 additions & 132 deletions denon.ts
Original file line number Diff line number Diff line change
@@ -1,110 +1,12 @@
import {
dirname,
exists,
extname,
parse,
resolve,
} from "./deps.ts";
import { dirname, exists, extname, resolve } from "./deps.ts";
import { parseArgs, help, applyIfDefined } from "./cli.ts";
import { DenonConfig, DenonConfigDefaults, readConfig } from "./denonrc.ts";
import { debug, log, fail, setConfig } from "./log.ts";
import Watcher from "./watcher.ts";

export let config: DenonConfig = DenonConfigDefaults;
setConfig(config);

function help() {
console.log(`
Usage:
denon [OPTIONS] [DENO_ARGS] [SCRIPT] [-- <SCRIPT_ARGS>]
OPTIONS:
-c, --config <file> A path to a config file, defaults to [default: .denonrc | .denonrc.json]
-d, --debug Debugging mode for more verbose logging
-e, --extensions List of extensions to look for separated by commas
-f, --fullscreen Clears the screen each reload
-h, --help Prints this
-i, --interval <ms> The number of milliseconds between each check
-m, --match <glob> Glob pattern for all the files to match
-q, --quiet Turns off all logging
-s, --skip <glob> Glob pattern for ignoring specific files or directories
-w, --watch List of paths to watch separated by commas
DENO_ARGS: Arguments passed to Deno to run SCRIPT (like permisssions)
`);
}

export interface Args {
config?: string;
extensions?: string;
interval?: string;
match?: string;
skip?: string;
watch?: string;
debug?: boolean;
fullscreen?: boolean;
help?: boolean;
quiet?: boolean;
runnerFlags: string[];
deno_args: string[];
files: string[];
}

export function parseArgs(args: string[]): Args {
if (args[0] === "--") {
args = args.slice(1);
}

let deno_args: string[] = [];

const flags = parse(args, {
string: ["config", "extensions", "interval", "match", "skip", "watch"],
boolean: ["debug", "fullscreen", "help", "quiet"],
alias: {
config: "c",
debug: "d",
extensions: "e",
fullscreen: "f",
help: "h",
interval: "i",
match: "m",
quiet: "q",
skip: "s",
watch: "w",
},
"--": true,
unknown: (arg: string, k?: string, v?: unknown) => {
deno_args.push(arg);
if (v && !arg.endsWith(String(v)) && typeof (v) !== "boolean") {
deno_args.push(String(v));
}
return false;
},
});

const files: string[] = [];
const script = deno_args[deno_args.length - 1];
if (script && !script.startsWith("-")) {
files.push(script);
deno_args = deno_args.slice(0, -1);
}

return {
config: flags.config,
debug: flags.debug,
extensions: flags.extensions,
fullscreen: flags.fullscreen,
help: flags.help,
interval: flags.interval,
match: flags.match,
quiet: flags.quiet,
skip: flags.skip,
watch: flags.watch,
runnerFlags: flags["--"],
files,
deno_args,
};
}

if (import.meta.main) {
const flags = parseArgs(Deno.args);

Expand All @@ -131,39 +33,22 @@ if (import.meta.main) {
Deno.exit(0);
}

debug(`Config: ${JSON.stringify(config)}`);
applyIfDefined(
flags,
config,
[
"deno_args",
"extensions",
"fullscreen",
"interval",
"match",
"quiet",
"skip",
"watch",
],
);

if (flags.extensions) {
config.extensions = flags.extensions.split(",");
}

if (flags.fullscreen) {
config.fullscreen = flags.fullscreen;
}

if (flags.interval) {
config.interval = parseInt(flags.interval, 10);
}

if (flags.match) {
config.match = [flags.match];
}

if (flags.watch) {
config.watch = flags.watch.split(",");
}

if (flags.quiet) {
config.quiet = flags.quiet;
}

if (flags.skip) {
config.skip = [flags.skip];
}

if (flags.deno_args.length) {
config.deno_args = flags.deno_args;
}
debug(`Config: ${JSON.stringify(config)}`);

if (config.files.length < 1 && flags.files.length < 1) {
fail(
Expand Down

0 comments on commit 2f79aae

Please sign in to comment.