Skip to content

Commit

Permalink
fix: fix arguments w/ rest have unknown type (#19)
Browse files Browse the repository at this point in the history
closes #18
  • Loading branch information
jaredLunde authored Mar 11, 2023
1 parent 5677211 commit 036d3e3
Show file tree
Hide file tree
Showing 27 changed files with 515 additions and 260 deletions.
26 changes: 18 additions & 8 deletions args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export function args(

return description && [...dedent(description)].join(" ");
},

long(context: any) {
let description: string | undefined;

Expand All @@ -37,7 +36,17 @@ export function args(

return description && [...dedent(description)].join("\n");
},
usage: config.use,
usage(context: any) {
let usage: string | undefined;

if (typeof config.use === "function") {
usage = config.use(context);
} else {
usage = config.use;
}

return usage;
},
__args: true,
};

Expand Down Expand Up @@ -114,15 +123,15 @@ export type Args =
/**
* A usage string for the arguments.
*/
usage?: string;
usage<Context extends BaseContext>(context: Context): string | undefined;
__args: true;
};

export type ArgsZodTypes =
| z.ZodTuple
| z.ZodTuple<any, any>
| z.ZodArray<any>
| z.ZodDefault<z.ZodTuple | z.ZodArray<any>>
| z.ZodOptional<z.ZodTuple | z.ZodArray<any>>;
| z.ZodDefault<z.ZodTuple<any, any> | z.ZodArray<any>>
| z.ZodOptional<z.ZodTuple<any, any> | z.ZodArray<any>>;

export type ArgsConfig = {
/**
Expand All @@ -136,10 +145,11 @@ export type ArgsConfig = {
/**
* A usage string for the arguments.
*/
use?: string;
use?: string | (<Context extends BaseContext>(context: Context) => string);
};

export type ArgTypes = Pick<typeof z, "tuple" | "array">;
export type inferArgs<T extends Args | ArgsZodTypes> = T extends
z.ZodOptional<z.ZodTuple | z.ZodArray<any>> ? [] | NonNullable<T["_output"]>
z.ZodOptional<z.ZodTuple<any, any> | z.ZodArray<any>>
? [] | NonNullable<T["_output"]>
: T["_output"];
2 changes: 1 addition & 1 deletion bench/flags-parser.bench.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as flags from "https://deno.land/std@0.178.0/flags/mod.ts";
import * as flags from "https://deno.land/std@0.179.0/flags/mod.ts";
import { parse } from "../flags-parser.ts";

console.log(
Expand Down
105 changes: 62 additions & 43 deletions command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export function command<
Context extends DefaultContext,
Args extends
| ArgsTuple
| ArgsZodTypes
| unknown = unknown,
Opts extends Flags | unknown = unknown,
>(
Expand Down Expand Up @@ -71,51 +72,29 @@ export function command<
| undefined;
const hasCmds = !!commands?.length;

function* help(context: Context): Iterable<string> {
function* usage(context: Context): Iterable<string> {
const displayName = context.path.join(" ") || name;

if (long || short) {
const desc = typeof long === "function"
? long(context)
: long
? long
: typeof short === "function"
? short(context)
: short + "";

for (const line of dedent(desc)) {
yield line;
}

yield "";
}

if (deprecated) {
yield colors.bold(colors.red("Deprecated"));

for (const line of dedent(deprecated)) {
yield ` ${line}`;
}

yield "";
}

yield colors.bold("Usage");

const hasAvailableCmds = hasCmds && commands.some((cmd) => !cmd.hidden);

if (use) {
yield ` ${use}`;
yield ` ${
typeof use === "function" ? [...dedent(use(context))].join("\n ") : use
}`;
} else {
if (hasAvailableCmds) {
yield ` ${displayName} [command]`;
}

if (args) {
let argsUsage = ` ${displayName}`;

if (typeof args === "object" && "usage" in args && args.usage) {
argsUsage += ` ${args.usage}`;
// @ts-expect-error: all good
const usage = typeof args.usage === "function"
// @ts-expect-error: all good
? args.usage(context)
: "";

if (usage) {
argsUsage += ` ${[...dedent(usage)].join("\n ")}`;
} else {
const hasOptionalArgs = args instanceof z.ZodDefault ||
args instanceof z.ZodOptional ||
Expand All @@ -139,6 +118,43 @@ export function command<
yield ` ${displayName} [flags]`;
}
}
}

function* help(context: Context): Iterable<string> {
const displayName = context.path.join(" ") || name;
const hasAvailableCmds = hasCmds && commands.some((cmd) => !cmd.hidden);

if (long || short) {
const desc = typeof long === "function"
? long(context)
: long
? long
: typeof short === "function"
? short(context)
: short + "";

for (const line of dedent(desc)) {
yield line;
}

yield "";
}

if (deprecated) {
yield colors.bold(colors.red("Deprecated"));

for (const line of dedent(deprecated)) {
yield ` ${line}`;
}

yield "";
}

yield colors.bold("Usage");

for (const line of usage(context)) {
yield line;
}

if (aliases.length) {
yield colors.bold("\nAliases");
Expand Down Expand Up @@ -247,15 +263,18 @@ export function command<
aliases,
commands: commands ?? [],
// @ts-expect-error: so dumb
args: args,
args,
// @ts-expect-error: so dumb
flags: flags ?? {},
hidden: hidden || typeof deprecated === "string",
deprecated,
help,
usage: use,
meta,

usage(ctx) {
return [...usage(ctx)].join("\n");
},

short(context) {
let description: string | undefined;

Expand Down Expand Up @@ -466,14 +485,14 @@ export function command<
if (e.code === z.ZodIssueCode.too_small) {
return `expected at least ${
intl.plural(
e.minimum,
Number(e.minimum),
"argument",
)
} arguments`;
} else if (e.code === z.ZodIssueCode.too_big) {
return `expected at most ${
intl.plural(
e.maximum,
Number(e.maximum),
"argument",
)
}`;
Expand Down Expand Up @@ -597,6 +616,7 @@ export type Command<
Context extends DefaultContext,
Args extends
| ArgsTuple
| ArgsZodTypes
| unknown = unknown,
Opts extends Flags | unknown = unknown,
GlobalOpts extends Flags | unknown = unknown,
Expand Down Expand Up @@ -640,7 +660,7 @@ export type Command<
/**
* The usage string for the command
*/
usage?: Readonly<string>;
usage(context: Context): string;
/**
* A short description of the command
*/
Expand Down Expand Up @@ -707,6 +727,7 @@ export type CommandConfig<
Context extends BaseContext = DefaultContext,
Args extends
| ArgsTuple
| ArgsZodTypes
| unknown = unknown,
Opts extends Flags | unknown = unknown,
> = {
Expand Down Expand Up @@ -738,7 +759,7 @@ export type CommandConfig<
/**
* Command usage
*/
use?: string;
use?: string | ((context: Context) => string);
/**
* A short description of the command
*/
Expand Down Expand Up @@ -795,9 +816,7 @@ export type PersistentAction<

export type Action<
Context extends DefaultContext,
Args extends
| ArgsTuple
| unknown = unknown,
Args extends ArgsTuple | ArgsZodTypes | unknown = unknown,
Opts extends Flags | unknown = unknown,
GlobalOpts extends Flags | unknown = unknown,
> = {
Expand Down
30 changes: 18 additions & 12 deletions completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,27 +44,29 @@ export function completion<
commands: [
commandFactory.command("bash", {
short: "Generate an autocompletion script for the bash shell",
long: ({ root }) => `
long: ({ root, path }) => `
Generate the autocompletion script for the bash shell.
This script depends on the \`bash-completion\` package.
If it is not installed already, you can install it via your OS's package manager.
To load completions in your current shell session:
\`\`\`
$ source <(${root.name} ${name} bash)
$ source <(${path.join(" ")} bash)
\`\`\`
To load completions for every new session, execute once:
Linux:
\`\`\`
$ ${root.name} ${name} bash > /etc/bash_completion.d/${root.name}
$ ${path.join(" ")} bash > /etc/bash_completion.d/${root.name}
\`\`\`
MacOS:
macOS:
\`\`\`
$ ${root.name} ${name} bash > /usr/local/etc/bash_completion.d/${root.name}
$ ${
path.join(" ")
} bash > /usr/local/etc/bash_completion.d/${root.name}
\`\`\`
You will need to start a new shell for this setup to take effect.
Expand All @@ -74,7 +76,7 @@ export function completion<
}),
commandFactory.command("zsh", {
short: "Generate an autocompletion script for the zsh shell",
long: ({ root }) => `
long: ({ root, path }) => `
Generate the autocompletion script for the zsh shell.
If shell completion is not already enabled in your environment you will need
Expand All @@ -88,17 +90,19 @@ export function completion<
Linux:
\`\`\`
$ ${root.name} ${name} zsh > "\${fpath[1]}/_${root.name}"
$ ${path.join(" ")} zsh > "\${fpath[1]}/_${root.name}"
\`\`\`
macOS:
\`\`\`
$ ${root.name} ${name} zsh > /usr/local/share/zsh/site-functions/_${root.name}
$ ${
path.join(" ")
} zsh > /usr/local/share/zsh/site-functions/_${root.name}
\`\`\`
Oh My Zsh:
\`\`\`
$ ${root.name} ${name} zsh > ~/.oh-my-zsh/completions/_${root.name}
$ ${path.join(" ")} zsh > ~/.oh-my-zsh/completions/_${root.name}
\`\`\`
You will need to start a new shell for this setup to take effect.
Expand All @@ -115,17 +119,19 @@ export function completion<
}),
commandFactory.command("fish", {
short: "Generate an autocompletion script for the fish shell",
long: ({ root }) => `
long: ({ root, path }) => `
Generate the autocompletion script for the fish shell.
To load completions in your current shell session:
\`\`\`
$ ${root.name} ${name} fish | source
$ ${path.join(" ")} fish | source
\`\`\`
To load completions for every new session, execute once:
\`\`\`
$ ${root.name} ${name} fish > ~/.config/fish/completions/${root.name}.fish
$ ${
path.join(" ")
} fish > ~/.config/fish/completions/${root.name}.fish
\`\`\`
You will need to start a new shell for this setup to take effect.
Expand Down
9 changes: 2 additions & 7 deletions config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
// deno-lint-ignore-file no-explicit-any
import * as YAML from "https://deno.land/[email protected]/encoding/yaml.ts";
import * as TOML from "https://deno.land/[email protected]/encoding/toml.ts";
import * as JSONc from "https://deno.land/[email protected]/encoding/jsonc.ts";
import * as INI from "https://deno.land/x/[email protected]/mod.ts";
import * as path from "https://deno.land/[email protected]/path/mod.ts";
import { JSONc, path, TOML, YAML } from "./deps.ts";
import { Join, NestedKeys, NestedValue, Split } from "./lib/types.ts";
import { z } from "./z.ts";

Expand Down Expand Up @@ -111,7 +107,6 @@ export const parsers = {
},
yaml: YAML,
toml: TOML,
ini: INI,
} as const;

export const configUtil = {
Expand Down Expand Up @@ -198,7 +193,7 @@ export type ConfigOptions<Schema extends z.ZodRawShape> = {
/**
* @default "toml"
*/
format?: "jsonc" | "yaml" | "toml" | "ini";
format?: "jsonc" | "yaml" | "toml";
/**
* The write mode for the config file.
* @default 0o600
Expand Down
Loading

0 comments on commit 036d3e3

Please sign in to comment.