Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions packages/pretty-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ prettyCLI
Pretty CLI also provides a common UI interface, providing log functions to output coloured messages to the UI:

```
const ui = require('@tryghost/pretty-cli/ui');`
const ui = require('@tryghost/pretty-cli/lib/ui');

ui.log.info('Done');
ui.log.warn('Uh Oh', 'Something went wrong');
Expand All @@ -49,10 +49,21 @@ You can also grab a fresh instance of the api with `prettyCLI.Api.get()`.

The style rules used are available at `prettyCLI.styles`.

## TypeScript

The package includes declarations for its standard command, option, help and
output APIs, including the existing `lib/` entry points. Both ESM imports and
CommonJS `import = require()` are supported. Runtime code remains CommonJS.

Parsed argument values are `unknown` because sywac's option configuration is
dynamic. Narrow them before use; command `setup` callbacks receive the typed API
and `parse()` returns a typed result. These declarations do not model sywac's
internal type factories.

## Test

- `pnpm lint` runs oxlint
- `pnpm test` runs lint and tests
- `pnpm test` runs type checks, runtime tests and lint

# Copyright & License

Expand Down
3 changes: 3 additions & 0 deletions packages/pretty-cli/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import prettyCli = require('./lib/pretty-cli');

export = prettyCli;
197 changes: 197 additions & 0 deletions packages/pretty-cli/lib/pretty-cli.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import ui = require('./ui');
import styles = require('./styles');

declare namespace prettyCli {
/** Parsed arguments are dynamic; narrow values before using them. */
type Args = Record<string, unknown>;
type Flags = string | string[];
type Version = string | (() => string);
type Style = (text: string, details?: unknown) => string;
type Styles = Partial<
Record<
| 'group'
| 'groupError'
| 'flags'
| 'flagsError'
| 'desc'
| 'descError'
| 'hints'
| 'hintsError'
| 'messages'
| 'usagePrefix'
| 'usagePositionals'
| 'usageCommandPlaceholder'
| 'usageArgsPlaceholder'
| 'usageOptionsPlaceholder'
| 'example'
| 'all',
Style
>
>;
type OutputSettings = {
lineSep?: string;
sectionSep?: string;
pad?: string;
indent?: string;
split?: string | RegExp;
maxWidth?: number;
examplePrefix?: string;
showHelpOnError?: boolean;
};
type ApiOptions = {
helpOpts?: OutputSettings;
name?: string;
parentName?: string;
strictMode?: boolean;
showHelpByDefault?: boolean;
};
type OptionOptions = {
flags?: string;
aliases?: Flags;
type?: string;
desc?: string;
description?: string;
defaultValue?: unknown;
required?: boolean;
strict?: boolean;
hidden?: boolean;
hints?: string;
group?: string;
choices?: unknown[];
version?: Version;
mustExist?: boolean;
dirAllowed?: boolean;
fileAllowed?: boolean;
normalize?: boolean;
asObject?: boolean;
asPosix?: boolean;
asWin32?: boolean;
coerce?: (value: unknown) => unknown;
};
type PositionalParameters = Array<string | OptionOptions> | Record<string, OptionOptions>;
type PositionalOptions = OptionOptions & {
params?: PositionalParameters;
paramsDescription?: string | string[];
paramsDesc?: string | string[];
paramsGroup?: string;
ignore?: string[];
};
type CommandHandler = (argv: Args, context: Context) => unknown;
type CommandOptions = PositionalOptions & {
setup?: (api: Api) => void;
run?: CommandHandler;
};
type CommandDirectoryOptions = CommandOptions & { extensions?: string[] };
type CommandDefinition = CommandOptions & ({ flags: string } | { aliases: Flags });
type ExampleOptions = { flags?: string; desc?: string; description?: string; group?: string };
type UsageOptions = {
usage?: string;
prefix?: string;
commandPlaceholder?: string;
argsPlaceholder?: string;
optionsPlaceholder?: string;
};
type Details = { args: string[]; types: unknown[] };
type ParseResult = {
code: number;
output: string;
errors: unknown[];
argv: Args;
details: Details;
};
type Context = ParseResult & {
commandHandlerRun: boolean;
helpRequested: boolean | OutputSettings;
versionRequested: boolean | { version?: Version };
cliMessage(message: unknown, ...args: unknown[]): void;
deferHelp(options?: OutputSettings): Context;
};
type Api = CliApi;
}

declare class CliApi {
constructor(options?: prettyCli.ApiOptions);
static get(options?: prettyCli.ApiOptions): CliApi;
static readonly DEFAULT_COMMAND_INDICATOR: string;
readonly name: string;
readonly parentName: string;
readonly types: unknown[];

configure(options?: prettyCli.ApiOptions): this;
newChild(name: string, options?: prettyCli.ApiOptions): CliApi;
strict(enabled?: boolean): this;
showHelpByDefault(enabled?: boolean): this;
preface(icon: string, slogan?: string): this;
usage(usage: string | prettyCli.UsageOptions): this;
groupOrder(groups?: string[]): this;
example(example: string | prettyCli.ExampleOptions, options?: prettyCli.ExampleOptions): this;
exampleOrder(groups?: string[]): this;
epilogue(text: string): this;
outputSettings(settings: prettyCli.OutputSettings): this;
style(hooks: prettyCli.Styles): this;

command(options: prettyCli.CommandDefinition): this;
command(flags: string, options?: prettyCli.CommandOptions | prettyCli.CommandHandler): this;
commandDirectory(directory: string, options?: prettyCli.CommandDirectoryOptions): this;
commandDirectory(options?: prettyCli.CommandDirectoryOptions): this;
positional(
flags: string | prettyCli.PositionalParameters | prettyCli.PositionalOptions,
options?: prettyCli.PositionalOptions,
): this;
option(
flags: prettyCli.Flags | prettyCli.OptionOptions,
options?: prettyCli.OptionOptions,
): this;
boolean(
flags: prettyCli.Flags | prettyCli.OptionOptions,
options?: prettyCli.OptionOptions,
): this;
string(
flags: prettyCli.Flags | prettyCli.OptionOptions,
options?: prettyCli.OptionOptions,
): this;
number(
flags: prettyCli.Flags | prettyCli.OptionOptions,
options?: prettyCli.OptionOptions,
): this;
path(flags: prettyCli.Flags | prettyCli.OptionOptions, options?: prettyCli.OptionOptions): this;
file(flags: prettyCli.Flags | prettyCli.OptionOptions, options?: prettyCli.OptionOptions): this;
dir(flags: prettyCli.Flags | prettyCli.OptionOptions, options?: prettyCli.OptionOptions): this;
enumeration(
flags: prettyCli.Flags | prettyCli.OptionOptions,
options?: prettyCli.OptionOptions,
): this;
help(
flags?: prettyCli.Flags | prettyCli.OptionOptions,
options?: prettyCli.OptionOptions,
): this;
version(
flags?: prettyCli.Flags | prettyCli.OptionOptions,
options?: prettyCli.OptionOptions,
): this;
array(
flags: prettyCli.Flags | prettyCli.OptionOptions,
options?: prettyCli.OptionOptions,
): this;
stringArray(
flags: prettyCli.Flags | prettyCli.OptionOptions,
options?: prettyCli.OptionOptions,
): this;
numberArray(
flags: prettyCli.Flags | prettyCli.OptionOptions,
options?: prettyCli.OptionOptions,
): this;
check(handler: prettyCli.CommandHandler): this;
parse(args?: string | string[]): Promise<prettyCli.ParseResult>;
parseAndExit(args?: string | string[]): Promise<prettyCli.Args>;
getHelp(options?: prettyCli.OutputSettings): string;
}

/** The styled process-wide instance; Api.get() creates an unstyled fresh instance. */
declare const prettyCli: CliApi & {
Api: typeof CliApi;
styles: typeof styles;
ui: typeof ui;
};

export = prettyCli;
13 changes: 13 additions & 0 deletions packages/pretty-cli/lib/styles.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
declare const styles: {
usagePrefix(text: string): string;
group(text: string): string;
flags(text: string): string;
hints(text: string): string;
groupError(text: string): string;
flagsError(text: string): string;
descError(text: string): string;
hintsError(text: string): string;
messages(text: string): string;
};

export = styles;
12 changes: 12 additions & 0 deletions packages/pretty-cli/lib/ui.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
type Log = ((...args: unknown[]) => void) & {
ok(...args: unknown[]): void;
trace(...args: unknown[]): void;
debug(...args: unknown[]): void;
info(...args: unknown[]): void;
warn(...args: unknown[]): void;
error(...args: unknown[]): void;
fatal(...args: unknown[]): void;
};

declare const ui: { log: Log };
export = ui;
10 changes: 7 additions & 3 deletions packages/pretty-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,27 @@
},
"files": [
"index.js",
"index.d.ts",
"lib"
],
"main": "index.js",
"types": "index.d.ts",
"publishConfig": {
"access": "public"
},
"scripts": {
"dev": "echo \"Implement me!\"",
"test": "NODE_ENV=testing vitest run --config ../../vitest.config.ts --root . --coverage",
"test": "pnpm test:types && NODE_ENV=testing vitest run --config ../../vitest.config.ts --root . --coverage",
"lint": "oxlint -c ../../.oxlintrc.json .",
"posttest": "pnpm run lint"
"posttest": "pnpm run lint",
"test:types": "tsc --noEmit -p test/types/tsconfig.json"
},
"dependencies": {
"chalk": "^6.0.0",
"sywac": "^1.3.0"
},
"devDependencies": {
"sinon": "catalog:"
"sinon": "catalog:",
"typescript": "catalog:"
}
}
Loading
Loading