Socket
Socket
Sign inDemoInstall

clipanion

Package Overview
Dependencies
Maintainers
1
Versions
84
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

clipanion - npm Package Compare versions

Comparing version 2.0.0-rc.10 to 2.0.0-rc.11

lib/advanced/HelpCommand.d.ts

47

lib/advanced/Cli.d.ts
/// <reference types="node" />
import { Readable, Writable } from 'stream';
import { CommandClass, Command } from './Command';
export interface DefaultContext {
export declare type BaseContext = {
stdin: Readable;
stdout: Writable;
stderr: Writable;
}
export interface MiniCli<Context extends DefaultContext> {
process(argv: string[]): Command<Context>;
run(argv: string[], subContext?: Partial<Context>): Promise<number | void>;
}
export declare class Cli<Context extends DefaultContext = DefaultContext> {
private readonly coreCli;
private readonly binaryName;
private readonly binaryVersion;
};
export declare type CliContext<Context extends BaseContext> = {
commandClass: CommandClass<Context>;
};
export declare class Cli<Context extends BaseContext = BaseContext> {
private readonly builder;
private readonly registrations;
readonly binaryName: string;
readonly binaryVersion?: string;
constructor({ binaryName, binaryVersion }?: {

@@ -22,12 +21,11 @@ binaryName?: string;

});
register(advancedCommand: CommandClass<Context>): void;
private registerVersion;
private registerGeneralHelp;
private registerDefinitions;
start(): import("../core/Machine").Machine<(cli: MiniCli<Context>, context: Context) => Command<Context>>;
process(argv: string[]): (cli: MiniCli<Context>, context: Context) => Command<Context>;
run(argv: string[], context: Context): Promise<number>;
runExit(argv: string[], context: Context): Promise<void>;
usage(command: Command<Context> | null, { detailed }?: {
getUsageByRegistration(klass: CommandClass<Context>): string;
getUsageByIndex(n: number): string;
register(commandClass: CommandClass<Context>): void;
process(input: string[]): Command<Context>;
run(input: string[], context: Context): Promise<number>;
runExit(input: string[], context: Context): Promise<void>;
usage(command: CommandClass<Context> | Command<Context> | null, { detailed, prefix }?: {
detailed?: boolean;
prefix?: string;
}): string;

@@ -38,1 +36,12 @@ error(error: Error, { command }?: {

}
export declare type MiniCli<Context extends BaseContext> = {
process(input: string[]): Command<Context>;
run(input: string[], context?: Partial<Context>): Promise<number>;
error(error: Error, opts?: {
command?: Command<Context> | null;
}): string;
usage(command: CommandClass<Context> | Command<Context> | null, opts?: {
detailed?: boolean;
prefix?: string;
}): string;
};
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const chalk_1 = __importDefault(require("chalk"));
const core = __importStar(require("../core"));
const pretty_1 = require("../pretty");
const Command_1 = require("./Command");
const core_1 = require("../core");
const core_2 = require("../core");
const format_1 = require("../format");
const HelpCommand_1 = require("./HelpCommand");
class Cli {
constructor({ binaryName = `...`, binaryVersion = `no version specified` } = {}) {
this.coreCli = new core.Cli();
this.registrations = [];
constructor({ binaryName = `...`, binaryVersion } = {}) {
this.registrations = new Map();
this.builder = new core_2.CliBuilder({ binaryName });
this.binaryName = binaryName;
this.binaryVersion = binaryVersion;
this.registerVersion();
this.registerDefinitions();
this.registerGeneralHelp();
}
register(advancedCommand) {
const coreCommand = advancedCommand.compile();
this.registrations.push({ advancedCommand, coreCommand });
this.coreCli.register(coreCommand);
getUsageByRegistration(klass) {
const index = this.registrations.get(klass);
if (typeof index === `undefined`)
throw new Error(`Assertion failed: Unregistered command`);
return this.getUsageByIndex(index);
}
registerVersion() {
const self = this;
class Version extends Command_1.Command {
async execute() {
this.context.stdout.write(`${self.binaryVersion}\n`);
}
}
__decorate([
Command_1.Command.HideFromErrors()
], Version.prototype, "execute", null);
const coreCommand = Version.compile();
// We replace the link function so that we can inject a special logic
// into the state machine (instead of the default one which would check
// the path, options, etc).
coreCommand.link = function ({ proxyStart }) {
if (typeof proxyStart === `undefined` || proxyStart >= 1) {
const versionNode = this.createNode({ weight: 0, label: `activate the version` });
this.registerTransition(core.NODE_INITIAL, `-v`, versionNode);
this.registerTransition(core.NODE_INITIAL, `--version`, versionNode);
this.markTerminal(versionNode);
}
};
this.coreCli.register(coreCommand);
getUsageByIndex(n) {
return this.builder.getBuilderByIndex(n).usage();
}
registerGeneralHelp() {
const self = this;
class GeneralHelpListing extends Command_1.Command {
async execute() {
const commandsByCategories = new Map();
let maxPathLength = 0;
for (const { advancedCommand, coreCommand } of self.registrations) {
if (typeof advancedCommand.usage === `undefined`)
continue;
const category = typeof advancedCommand.usage.category !== `undefined`
? pretty_1.prettyMarkdownish(advancedCommand.usage.category, false)
: undefined;
let categoryCommands = commandsByCategories.get(category);
if (typeof categoryCommands === `undefined`)
commandsByCategories.set(category, categoryCommands = []);
const path = coreCommand.definition.path.join(` `);
categoryCommands.push({ command: advancedCommand, path });
const pathLength = path.length;
if (pathLength > maxPathLength) {
maxPathLength = pathLength;
}
register(commandClass) {
const commandBuilder = this.builder.command();
this.registrations.set(commandClass, commandBuilder.cliIndex);
const { definitions } = commandClass.getMeta(commandClass.prototype);
for (const definition of definitions)
definition(commandBuilder);
commandBuilder.setContext({
commandClass,
});
}
process(input) {
const { contexts, process } = this.builder.compile();
const state = process(input);
switch (state.selectedIndex) {
case core_1.HELP_COMMAND_INDEX:
{
return HelpCommand_1.HelpCommand.from(state, this, contexts);
}
const categoryNames = Array.from(commandsByCategories.keys()).sort((a, b) => {
if (a === undefined)
return -1;
if (b === undefined)
return +1;
return a.localeCompare(b, `en`, { usage: `sort`, caseFirst: `upper` });
});
this.context.stdout.write(self.usage(null));
for (let categoryName of categoryNames) {
const commands = commandsByCategories.get(categoryName).slice().sort((a, b) => {
return a.path.localeCompare(b.path, `en`, { usage: `sort`, caseFirst: `upper` });
});
const header = typeof categoryName !== `undefined`
? categoryName.trim()
: `Where <command> is one of`;
this.context.stdout.write(`\n`);
this.context.stdout.write(`${chalk_1.default.bold(`${header}:`)}\n`);
this.context.stdout.write(`\n`);
const pad = (str) => `${str}${` `.repeat(maxPathLength - str.length)}`;
for (let { command, path } of commands) {
const doc = command.usage.description || `undocumented`;
this.context.stdout.write(` ${chalk_1.default.bold(pad(path))} ${pretty_1.prettyMarkdownish(doc, false)}`);
}
break;
default:
{
const { commandClass } = contexts[state.selectedIndex];
const command = new commandClass();
const { transformers } = commandClass.getMeta(commandClass.prototype);
for (const transformer of transformers)
transformer(state, command);
return command;
}
}
break;
}
__decorate([
Command_1.Command.HideFromErrors()
], GeneralHelpListing.prototype, "execute", null);
const coreCommand = GeneralHelpListing.compile();
// We replace the link function so that we can inject a special logic
// into the state machine (instead of the default one which would check
// the path, options, etc).
coreCommand.link = function ({ proxyStart }) {
if (typeof proxyStart === `undefined` || proxyStart >= 1) {
const generalHelpNode = this.createNode({ weight: 0, label: `activate the general help` });
this.registerTransition(core.NODE_INITIAL, `-h`, generalHelpNode);
this.registerTransition(core.NODE_INITIAL, `--help`, generalHelpNode);
this.markTerminal(generalHelpNode);
}
};
this.coreCli.register(coreCommand);
}
registerDefinitions() {
const self = this;
class DefinitionsCommand extends Command_1.Command {
async execute() {
const data = [];
for (const { advancedCommand, coreCommand } of self.registrations) {
if (typeof advancedCommand.usage === `undefined`)
continue;
const path = [self.binaryName, ...coreCommand.definition.path];
const usage = pretty_1.prettyCommand(coreCommand.definition, { binaryName: self.binaryName });
const category = typeof advancedCommand.usage.category !== `undefined`
? pretty_1.prettyMarkdownish(advancedCommand.usage.category, false)
: undefined;
const description = typeof advancedCommand.usage.description !== `undefined`
? pretty_1.prettyMarkdownish(advancedCommand.usage.description, false)
: undefined;
const details = typeof advancedCommand.usage.details !== `undefined`
? pretty_1.prettyMarkdownish(advancedCommand.usage.details, true)
: undefined;
const examples = typeof advancedCommand.usage.examples !== `undefined`
? advancedCommand.usage.examples.map(([label, cli]) => [pretty_1.prettyMarkdownish(label, false), pretty_1.prettyMarkdownish(cli, false)])
: undefined;
data.push({ path, usage, category, description, details, examples });
}
this.context.stdout.write(JSON.stringify(data, null, 2) + `\n`);
}
}
__decorate([
Command_1.Command.HideFromErrors()
], DefinitionsCommand.prototype, "execute", null);
const coreCommand = DefinitionsCommand.compile();
// We replace the link function so that we can inject a special logic
// into the state machine (instead of the default one which would check
// the path, options, etc).
coreCommand.link = function ({ proxyStart }) {
if (typeof proxyStart === `undefined` || proxyStart >= 1) {
const definitionNode = this.createNode({ weight: 1, label: `activate the definitions` });
this.registerTransition(core.NODE_INITIAL, `--clipanion=definitions`, definitionNode);
this.markTerminal(definitionNode);
}
};
this.coreCli.register(coreCommand);
}
start() {
return this.coreCli.start();
}
process(argv) {
return this.coreCli.process(argv);
}
async run(argv, context) {
const cli = {
process: (argv) => {
return this.process(argv)(cli, context);
},
run: (argv, subContext) => {
return this.run(argv, Object.assign({}, context, subContext));
},
};
async run(input, context) {
let command;
try {
command = this.coreCli.process(argv)(cli, context);
command = this.process(input);
}
catch (error) {
if (typeof error.setBinaryName !== `undefined`)
error.setBinaryName(this.binaryName);
context.stderr.write(this.error(error));
context.stdout.write(this.error(error));
return 1;

@@ -199,31 +71,86 @@ }

}
command.context = context;
command.cli = {
process: input => {
return this.process(input);
},
run: async (input, subContext) => {
const exitCode = await this.run(input, Object.assign({}, context, { subContext }));
return typeof exitCode !== `undefined` ? exitCode : 0;
},
usage: (command, opts) => {
return this.usage(command, opts);
},
error: (error, opts) => {
return this.error(error, opts);
},
};
let exitCode;
try {
exitCode = await command.execute();
exitCode = await command.validateAndExecute();
}
catch (error) {
context.stderr.write(this.error(error, { command }));
context.stdout.write(this.error(error, { command }));
return 1;
}
if (typeof exitCode === `undefined`)
exitCode = 0;
return exitCode;
}
async runExit(argv, context) {
process.exitCode = await this.run(argv, context);
async runExit(input, context) {
process.exitCode = await this.run(input, context);
}
usage(command, { detailed = false } = {}) {
usage(command, { detailed = false, prefix = `$ ` } = {}) {
// @ts-ignore
const commandClass = command !== null && typeof command.getMeta === `undefined`
? command.constructor
: command;
let result = ``;
if (!command) {
result += `${chalk_1.default.bold(`$`)} ${this.binaryName} <command>\n`;
if (!commandClass) {
const commandsByCategories = new Map();
let maxPathLength = 0;
for (const [commandClass, number] of this.registrations.entries()) {
if (typeof commandClass.usage === `undefined`)
continue;
const category = typeof commandClass.usage.category !== `undefined`
? format_1.formatMarkdownish(commandClass.usage.category, false)
: null;
let categoryCommands = commandsByCategories.get(category);
if (typeof categoryCommands === `undefined`)
commandsByCategories.set(category, categoryCommands = []);
const usage = this.getUsageByIndex(number);
categoryCommands.push({ commandClass, usage });
}
const categoryNames = Array.from(commandsByCategories.keys()).sort((a, b) => {
if (a === null)
return -1;
if (b === null)
return +1;
return a.localeCompare(b, `en`, { usage: `sort`, caseFirst: `upper` });
});
result += `${chalk_1.default.bold(prefix)}${this.binaryName} <command>\n`;
for (let categoryName of categoryNames) {
const commands = commandsByCategories.get(categoryName).slice().sort((a, b) => {
return a.usage.localeCompare(b.usage, `en`, { usage: `sort`, caseFirst: `upper` });
});
const header = categoryName !== null
? categoryName.trim()
: `Where <command> is one of`;
result += `\n`;
result += `${chalk_1.default.bold(`${header}:`)}\n`;
result += `\n`;
const pad = (str) => `${str}${` `.repeat(maxPathLength - str.length)}`;
for (let { commandClass, usage } of commands) {
const doc = commandClass.usage.description || `undocumented`;
result += ` ${chalk_1.default.bold(usage)}\n`;
result += ` ${format_1.formatMarkdownish(doc, false)}`;
}
}
}
else {
const { definition } = command.constructor.getMeta();
if (!detailed) {
result += `${chalk_1.default.bold(`$`)} ${pretty_1.prettyCommand(definition, { binaryName: this.binaryName })}\n`;
result += `${chalk_1.default.bold(prefix)}${this.getUsageByRegistration(commandClass)}\n`;
}
else {
const { description = ``, details = ``, examples = [], } = command.constructor.usage || {};
const { description = ``, details = ``, examples = [], } = commandClass.usage || {};
if (description !== ``) {
result += pretty_1.prettyMarkdownish(description, false).replace(/^./, $0 => $0.toUpperCase());
result += format_1.formatMarkdownish(description, false).replace(/^./, $0 => $0.toUpperCase());
result += `\n`;

@@ -235,3 +162,3 @@ }

}
result += `${chalk_1.default.bold(`$`)} ${pretty_1.prettyCommand(definition, { binaryName: this.binaryName })}\n`;
result += `${chalk_1.default.bold(prefix)}${this.getUsageByRegistration(commandClass)}\n`;
if (details !== ``) {

@@ -241,3 +168,3 @@ result += `\n`;

result += `\n`;
result += pretty_1.prettyMarkdownish(details, true);
result += format_1.formatMarkdownish(details, true);
}

@@ -244,0 +171,0 @@ if (examples.length > 0) {

@@ -1,56 +0,91 @@

import * as core from '../core';
import { DefaultContext, MiniCli } from './Cli';
export declare type Usage = Partial<{
category: string | undefined;
description: string;
details: string;
examples: [string, string][];
}>;
export declare type Meta = {
definition: core.Definition;
transformers: ((command: any, parsed: core.Parsed) => void)[];
import { CommandBuilder, RunState } from '../core';
import { BaseContext, CliContext, MiniCli } from './Cli';
export declare type Meta<Context extends BaseContext> = {
definitions: ((command: CommandBuilder<CliContext<Context>>) => void)[];
transformers: ((state: RunState, command: Command<Context>) => void)[];
};
export interface CommandClass<Context extends DefaultContext = DefaultContext> {
export declare type Usage = {
category?: string;
description?: string;
details?: string;
examples?: [string, string][];
};
export declare type CommandClass<Context extends BaseContext = BaseContext> = {
new (): Command<Context>;
compile(): core.Command<(cli: MiniCli<Context>, context: Context) => Command<Context>>;
getMeta(prototype: Command<Context>): Meta<Context>;
schema?: {
validate: (object: any) => void;
};
usage?: Usage;
}
export declare abstract class Command<Context extends DefaultContext = DefaultContext> {
static meta: Meta | undefined;
static getMeta<Context extends DefaultContext>(from?: Command<Context>): Meta;
static Validate(schema: any): (klass: any) => void;
static HideFromErrors(): <Context extends DefaultContext>(prototype: Command<Context>, propertyName: "execute") => void;
static Array(descriptor: string): <Context extends DefaultContext>(prototype: Command<Context>, propertyName: string) => void;
};
export declare abstract class Command<Context extends BaseContext = BaseContext> {
private static meta?;
static getMeta<Context extends BaseContext>(prototype: Command<Context>): Meta<Context>;
private static registerDefinition;
private static registerTransformer;
/**
* Wrap the specified command to be attached to the given path on the command line.
* The first path thus attached will be considered the "main" one, and all others will be aliases.
* @param path The command path.
*/
static Path(...path: string[]): <Context extends BaseContext>(prototype: Command<Context>, propertyName: string) => void;
/**
* Register a boolean listener for the given option names. When Clipanion detects that this argument is present, the value will be set to false. The value won't be set unless the option is found, so you must remember to set it to an appropriate default value.
* @param descriptor the option names.
*/
static Boolean(descriptor: string): <Context extends BaseContext>(prototype: Command<Context>, propertyName: string) => void;
/**
* Register a listener that looks for an option and its followup argument. When Clipanion detects that this argument is present, the value will be set to whatever follows the option in the input. The value won't be set unless the option is found, so you must remember to set it to an appropriate default value.
* Note that all methods affecting positional arguments are evaluated in the definition order; don't mess with it (for example sorting your properties in ascendent order might have adverse results).
* @param descriptor The option names.
*/
static String(descriptor: string): PropertyDecorator;
/**
* Register a listener that looks for positional arguments. When Clipanion detects that an argument isn't an option, it will put it in this property and continue processing the rest of the command line.
* Note that all methods affecting positional arguments are evaluated in the definition order; don't mess with it (for example sorting your properties in ascendent order might have adverse results).
* @param descriptor Whether or not filling the positional argument is required for the command to be a valid selection.
*/
static String(descriptor?: {
required: boolean;
}): PropertyDecorator;
/**
* Register a listener that takes all the positional arguments remaining and store them into the selected property.
* Note that all methods affecting positional arguments are evaluated in the definition order; don't mess with it (for example sorting your properties in ascendent order might have adverse results).
*/
static Rest(): PropertyDecorator;
/**
* Register a listener that takes all the positional arguments remaining and store them into the selected property.
* Note that all methods affecting positional arguments are evaluated in the definition order; don't mess with it (for example sorting your properties in ascendent order might have adverse results).
* @param opts.required The minimal number of arguments required for the command to be successful.
*/
static Rest(opts: {
required: number;
}): PropertyDecorator;
static Proxy(): <Context extends DefaultContext>(prototype: Command<Context>, propertyName: string) => void;
static Boolean(descriptor: string): <Context extends DefaultContext>(prototype: Command<Context>, propertyName: string) => void;
static String(): PropertyDecorator;
static String(opts: {
required: boolean;
}): PropertyDecorator;
static String(descriptor: string): PropertyDecorator;
static Path(...segments: string[]): <Context extends DefaultContext>(prototype: Command<Context>, propertyName: "execute") => void;
static Usage(usage: Usage): Partial<{
category: string | undefined;
description: string;
details: string;
examples: [string, string][];
}>;
static compile<Context extends DefaultContext>(): core.Command<(cli: MiniCli<Context>, context: Context) => Command<Context>>;
cli: MiniCli<Context>;
context: Context;
path: string[];
help: boolean;
static Proxy(): <Context extends BaseContext>(prototype: Command<Context>, propertyName: string) => void;
/**
* If defined, must contain a function that returns the string displayed
* when the command help message is generated.
* Defines the usage information for the given command.
* @param usage
*/
static Usage(usage: Usage): Usage;
/**
* Contains the usage information for the command. If undefined, the command will be hidden from the general listing.
*/
static usage?: Usage;
/**
* Executed by Cli#run and Cli#runExit.
* Standard command that'll get executed by `Cli#run` and `Cli#runExit`. Expected to return an exit code or nothing (which Clipanion will treat as if 0 had been returned).
*/
abstract execute(): Promise<number | void>;
validateAndExecute(): Promise<number>;
/**
* Predefined that will be set to true if `-h,--help` has been used, in which case `Command#execute` shouldn't be called.
*/
help: boolean;
/**
* Predefined variable that will be populated with a miniature API that can be used to query Clipanion and forward commands.
*/
cli: MiniCli<Context>;
/**
* Predefined variable that will be populated with the context of the application.
*/
context: Context;
}
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("../core"));
class Command {
constructor() {
// This option is automatically added (it needs an extra logic to support
// ignoring the overall syntax of the command)
/**
* Predefined that will be set to true if `-h,--help` has been used, in which case `Command#execute` shouldn't be called.
*/
this.help = false;
}
static getMeta(from) {
if (typeof from !== `undefined`)
return from.constructor.getMeta();
return this.meta = this.meta || {
definition: {
path: [],
options: {
simple: new Set(),
complex: new Set(),
},
positionals: {
minimum: 0,
maximum: 0,
proxy: false,
},
flags: {
hideFromErrors: false,
},
},
static getMeta(prototype) {
const base = prototype.constructor;
return base.meta = base.meta || {
usage: [],
definitions: [],
transformers: [
(command, parsed) => {
for (const { name, value } of parsed.options) {
(state, command) => {
for (const { name, value } of state.options) {
if (name === `-h` || name === `--help`) {
// @ts-ignore: The property is meant to have been defined by the child class
command.help = value;

@@ -47,81 +27,35 @@ }

}
static Validate(schema) {
return (klass) => {
const parent = klass.prototype.execute;
klass.prototype.execute = async function execute(cli) {
try {
await schema.validate(this);
}
catch (error) {
if (error.name === `ValidationError`)
error.clipanion = { type: `usage` };
throw error;
}
// @ts-ignore
return parent.call(this, cli);
};
};
static registerDefinition(prototype, definition) {
this.getMeta(prototype).definitions.push(definition);
}
static HideFromErrors() {
return (prototype, propertyName) => {
const { definition } = prototype.constructor.getMeta();
definition.flags.hideFromErrors = true;
};
static registerTransformer(prototype, transformer) {
this.getMeta(prototype).transformers.push(transformer);
}
static Array(descriptor) {
const optionNames = new Set(descriptor.split(/,/g));
/**
* Wrap the specified command to be attached to the given path on the command line.
* The first path thus attached will be considered the "main" one, and all others will be aliases.
* @param path The command path.
*/
static Path(...path) {
return (prototype, propertyName) => {
const { definition, transformers } = prototype.constructor.getMeta();
for (const optionName of optionNames)
definition.options.complex.add(optionName);
transformers.push((command, parsed) => {
for (const { name, value } of parsed.options) {
if (optionNames.has(name) && typeof value !== `undefined`) {
command[propertyName] = command[propertyName] || [];
command[propertyName].push(value);
}
}
this.registerDefinition(prototype, command => {
command.addPath(path);
});
};
}
static Rest({ required = 0 } = {}) {
/**
* Register a boolean listener for the given option names. When Clipanion detects that this argument is present, the value will be set to false. The value won't be set unless the option is found, so you must remember to set it to an appropriate default value.
* @param descriptor the option names.
*/
static Boolean(descriptor) {
return (prototype, propertyName) => {
const { definition, transformers } = prototype.constructor.getMeta();
const index = definition.positionals.maximum;
definition.positionals.minimum += required;
definition.positionals.maximum = Infinity;
transformers.push((command, parsed) => {
command[propertyName] = parsed.positionals.slice(index);
const optNames = descriptor.split(`,`);
this.registerDefinition(prototype, command => {
command.addOption({ names: optNames, arity: 0 });
});
};
}
static Proxy() {
return (prototype, propertyName) => {
const { definition, transformers } = prototype.constructor.getMeta();
const index = definition.positionals.maximum;
definition.positionals.maximum = Infinity;
definition.positionals.proxy = true;
transformers.push((command, parsed) => {
command[propertyName] = parsed.positionals.slice(index);
});
};
}
static Boolean(descriptor) {
const optionNames = new Set(descriptor.split(/,/g));
const reversedNames = new Set();
for (const optionName of descriptor)
if (optionName.startsWith(`--`) && !optionName.startsWith(`--no-`))
reversedNames.add(optionName);
return (prototype, propertyName) => {
const { definition, transformers } = prototype.constructor.getMeta();
for (const optionName of optionNames)
definition.options.simple.add(optionName);
for (const reversedName of reversedNames)
definition.options.simple.add(reversedName);
transformers.push((command, parsed) => {
for (const { name, value } of parsed.options) {
if (optionNames.has(name) && typeof value !== `undefined`)
this.registerTransformer(prototype, (state, command) => {
for (const { name, value } of state.options) {
if (optNames.includes(name)) {
// @ts-ignore: The property is meant to have been defined by the child class
command[propertyName] = value;
if (reversedNames.has(name) && typeof value !== `undefined`) {
command[propertyName] = !value;
}

@@ -134,10 +68,11 @@ }

return (prototype, propertyName) => {
const { definition, transformers } = prototype.constructor.getMeta();
if (typeof descriptor === `string`) {
const optionNames = new Set(descriptor.split(/,/));
for (const optionName of optionNames)
definition.options.complex.add(optionName);
transformers.push((command, parsed) => {
for (const { name, value } of parsed.options) {
if (optionNames.has(name) && typeof value !== `undefined`) {
const optNames = descriptor.split(`,`);
this.registerDefinition(prototype, command => {
command.addOption({ names: optNames, arity: 1 });
});
this.registerTransformer(prototype, (state, command) => {
for (const { name, value } of state.options) {
if (optNames.includes(name)) {
// @ts-ignore: The property is meant to have been defined by the child class
command[propertyName] = value;

@@ -149,39 +84,63 @@ }

else {
const index = definition.positionals.maximum;
definition.positionals.maximum += 1;
if (descriptor.required)
definition.positionals.minimum += 1;
transformers.push((command, parsed) => {
const value = parsed.positionals[index];
if (typeof value !== `undefined`) {
command[propertyName] = value;
}
this.registerDefinition(prototype, command => {
command.addPositional({ required: descriptor.required });
});
this.registerTransformer(prototype, (state, command) => {
// @ts-ignore: The property is meant to have been defined by the child class
command[propertyName] = state.positionals.shift().value;
});
}
};
}
static Path(...segments) {
static Rest({ required = 0 } = {}) {
return (prototype, propertyName) => {
const { definition } = prototype.constructor.getMeta();
definition.path = segments;
this.registerDefinition(prototype, command => {
command.addRest({ required });
});
this.registerTransformer(prototype, (state, command) => {
// @ts-ignore: The property is meant to have been defined by the child class
command[propertyName] = state.positionals.map(({ value }) => value);
});
};
}
static Proxy() {
return (prototype, propertyName) => {
this.registerDefinition(prototype, command => {
command.addProxy();
});
this.registerTransformer(prototype, (state, command) => {
// @ts-ignore: The property is meant to have been defined by the child class
command[propertyName] = state.positionals.map(({ value }) => value);
});
};
}
/**
* Defines the usage information for the given command.
* @param usage
*/
static Usage(usage) {
return usage;
}
static compile() {
const { definition, transformers } = this.getMeta();
return new core.Command(definition, parsed => (cli, context) => {
// @ts-ignore: In practice, "this" will be the subclass that
// inherit from Command (and thus not an abstract)
const bag = new this();
bag.cli = cli;
bag.context = context;
bag.path = parsed.path;
for (const transformer of transformers)
transformer(bag, parsed);
return bag;
});
async validateAndExecute() {
const commandClass = this.constructor;
const schema = commandClass.schema;
if (typeof schema !== `undefined`) {
try {
await schema.validate(this);
}
catch (error) {
if (error.name === `ValidationError`)
error.clipanion = { type: `usage` };
throw error;
}
}
const exitCode = await this.execute();
if (typeof exitCode !== `undefined`) {
return exitCode;
}
else {
return 0;
}
}
}
exports.Command = Command;

@@ -1,3 +0,2 @@

export { UsageError } from '../core';
export { Cli, DefaultContext } from './Cli';
export { CommandClass, Command } from './Command';
export * from './Cli';
export * from './Command';
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
var core_1 = require("../core");
exports.UsageError = core_1.UsageError;
var Cli_1 = require("./Cli");
exports.Cli = Cli_1.Cli;
var Command_1 = require("./Command");
exports.Command = Command_1.Command;
__export(require("./Cli"));
__export(require("./Command"));
{
"name": "clipanion",
"version": "2.0.0-rc.10",
"version": "2.0.0-rc.11",
"main": "lib/advanced",

@@ -5,0 +5,0 @@ "license": "MIT",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc