Comparing version 2.0.0-alpha-3 to 2.0.0
@@ -1,44 +0,15 @@ | ||
interface Argument<V> { | ||
key: string; | ||
description?: string; | ||
multi?: boolean; | ||
required?: boolean; | ||
default?: V; | ||
parse?: (val: string) => V; | ||
} | ||
interface Option<V> extends Argument<V> { | ||
type: 'option' | 'flag'; | ||
alias?: string; | ||
} | ||
interface Command { | ||
cmd: string; | ||
title?: string; | ||
description?: string; | ||
example?: string; | ||
help?: boolean; | ||
version?: string; | ||
options?: Option<any>[]; | ||
commands?: Command[]; | ||
commandRequired?: boolean; | ||
arguments?: Argument<any>[]; | ||
} | ||
interface Program extends Command { | ||
parseOnly?: boolean; | ||
} | ||
interface CommandArgs { | ||
cmd: string; | ||
options: Record<string, any>; | ||
command?: CommandArgs; | ||
} | ||
declare class ParseError extends Error { | ||
import { ParsedOptions, Program } from './types'; | ||
export declare class ParseError extends Error { | ||
name: string; | ||
program: CommandArgs; | ||
constructor(message: string, program: CommandArgs); | ||
program: ParsedOptions; | ||
constructor(message: string, program: ParsedOptions); | ||
} | ||
declare function createArgParser(config: Program): { | ||
parse: (args: string[]) => CommandArgs; | ||
run: () => ParsedOptions; | ||
parse: (args: string[]) => ParsedOptions; | ||
printError: (error: ParseError) => string; | ||
printUsage: (programArgs: CommandArgs) => string; | ||
printHelp: (programArgs: CommandArgs) => string; | ||
printUsage: (programArgs: ParsedOptions) => string; | ||
printHelp: (programArgs: ParsedOptions) => string; | ||
printVersion: () => string | undefined; | ||
}; | ||
export { createArgParser }; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createArgParser = void 0; | ||
exports.createArgParser = exports.ParseError = void 0; | ||
const print_1 = require("./print"); | ||
const initConfig_1 = require("./initConfig"); | ||
const validateParsedOptions_1 = require("./validateParsedOptions"); | ||
const OPTION = /^\-\-([^=]+)(?:(=)(.*)?)?$/; | ||
@@ -48,10 +51,11 @@ const OPTION_ALIAS = /^\-([^=])(?:(=)(.*)?)?$/; | ||
} | ||
exports.ParseError = ParseError; | ||
function parseArguments(args, config, | ||
// internal | ||
programArgs = { | ||
cmd: config.cmd, | ||
parsedOptions = { | ||
command: [config.cmd], | ||
options: {}, | ||
}, commandArgs = programArgs, currentConfig = config, argumentIndex = 0, index = 0, arg = args[index], next = args[index + 1]) { | ||
}, currentConfig = config, argumentIndex = 0, index = 0, arg = args[index], next = args[index + 1]) { | ||
if (index >= args.length) { | ||
return programArgs; | ||
return parsedOptions; | ||
} | ||
@@ -65,29 +69,29 @@ let skipNext = false; | ||
if (arg.hasValue) { | ||
throw new ParseError(`Flag ${arg.key} cannot specify a value.`, programArgs); | ||
throw new ParseError(`Flag ${arg.key} cannot specify a value.`, parsedOptions); | ||
} | ||
commandArgs.options[option.key] = true; | ||
parsedOptions.options[option.key] = true; | ||
} | ||
else if (arg.type === 'flag') { | ||
throw new ParseError(`Option ${option.key} cannot be used as a flag.`, programArgs); | ||
throw new ParseError(`Option ${option.key} cannot be used as a flag.`, parsedOptions); | ||
} | ||
else if (option.multi) { | ||
skipNext = getOptionValue(arg, next, programArgs); | ||
const array = (commandArgs.options[option.key] = | ||
commandArgs.options[option.key] || []); | ||
skipNext = getOptionValue(arg, next, parsedOptions); | ||
const array = (parsedOptions.options[option.key] = | ||
parsedOptions.options[option.key] || []); | ||
array.push(arg.value); | ||
} | ||
else { | ||
skipNext = getOptionValue(arg, next, programArgs); | ||
if (option.key in commandArgs.options) { | ||
throw new ParseError(`Option ${option.key} does not except multiple values.`, programArgs); | ||
skipNext = getOptionValue(arg, next, parsedOptions); | ||
if (option.key in parsedOptions.options) { | ||
throw new ParseError(`Option ${option.key} does not except multiple values.`, parsedOptions); | ||
} | ||
commandArgs.options[option.key] = arg.value; | ||
parsedOptions.options[option.key] = arg.value; | ||
} | ||
} | ||
else { | ||
throw new ParseError(`Unknown option ${arg.key}.`, programArgs); | ||
throw new ParseError(`Unknown option ${arg.key}.`, parsedOptions); | ||
} | ||
} | ||
else { | ||
throw new ParseError(`Unknown option ${arg.key}.`, programArgs); | ||
throw new ParseError(`Unknown option ${arg.key}.`, parsedOptions); | ||
} | ||
@@ -99,8 +103,4 @@ } | ||
if (command) { | ||
commandArgs.command = { | ||
cmd: command.cmd, | ||
options: {}, | ||
}; | ||
parsedOptions.command.push(command.cmd); | ||
// tslint:disable: no-parameter-reassignment | ||
commandArgs = commandArgs.command; | ||
currentConfig = command; | ||
@@ -112,11 +112,11 @@ argumentIndex = 0; | ||
if (!argument) { | ||
throw new ParseError(`Unexpected argument '${arg.value}'.`, programArgs); | ||
throw new ParseError(`Unexpected argument '${arg.value}'.`, parsedOptions); | ||
} | ||
if (argument.multi) { | ||
const array = (commandArgs.options[argument.key] = | ||
commandArgs.options[argument.key] || []); | ||
const array = (parsedOptions.options[argument.key] = | ||
parsedOptions.options[argument.key] || []); | ||
array.push(arg.value); | ||
} | ||
else { | ||
commandArgs.options[argument.key] = arg.value; | ||
parsedOptions.options[argument.key] = arg.value; | ||
argumentIndex += 1; | ||
@@ -126,3 +126,3 @@ } | ||
else { | ||
throw new ParseError(`Unexpected argument '${arg.value}'.`, programArgs); | ||
throw new ParseError(`Unexpected argument '${arg.value}'.`, parsedOptions); | ||
} | ||
@@ -133,11 +133,11 @@ } | ||
if (!argument) { | ||
throw new ParseError(`Unexpected argument '${arg.value}'.`, programArgs); | ||
throw new ParseError(`Unexpected argument '${arg.value}'.`, parsedOptions); | ||
} | ||
if (argument.multi) { | ||
const array = (commandArgs.options[argument.key] = | ||
commandArgs.options[argument.key] || []); | ||
const array = (parsedOptions.options[argument.key] = | ||
parsedOptions.options[argument.key] || []); | ||
array.push(arg.value); | ||
} | ||
else { | ||
commandArgs.options[argument.key] = arg.value; | ||
parsedOptions.options[argument.key] = arg.value; | ||
argumentIndex += 1; | ||
@@ -147,119 +147,39 @@ } | ||
else { | ||
throw new ParseError(`Unexpected argument '${arg.value}'.`, programArgs); | ||
throw new ParseError(`Unexpected argument '${arg.value}'.`, parsedOptions); | ||
} | ||
return parseArguments(args, config, | ||
// internal | ||
programArgs, commandArgs, currentConfig, argumentIndex, index + (skipNext ? 2 : 1)); | ||
parsedOptions, currentConfig, argumentIndex, index + (skipNext ? 2 : 1)); | ||
} | ||
function validateProgramArgs(programArgs, config) { | ||
if (config.options) { | ||
for (const option of config.options) { | ||
const arg = programArgs.options[option.key]; | ||
if (arg != null) { | ||
programArgs.options[option.key] = option.parse | ||
? option.multi | ||
? arg.map(option.parse) | ||
: option.parse(arg) | ||
: arg; | ||
function createArgParser(config) { | ||
const program = initConfig_1.initConfig(config); | ||
function run() { | ||
try { | ||
const args = process.argv.slice(2); | ||
const parsedOptions = parseArguments(args | ||
.map(parseArg) | ||
.reduce((acc, next) => acc.concat(next), []), program); | ||
if (parsedOptions.options.help) { | ||
console.log(printHelp(parsedOptions)); | ||
process.exit(0); | ||
} | ||
else if (option.required) { | ||
throw new ParseError(`Missing required option ${option.key}.`, programArgs); | ||
else if (parsedOptions.options.version) { | ||
console.log(printVersion()); | ||
process.exit(0); | ||
} | ||
else if (option.type === 'flag') { | ||
programArgs.options[option.key] = false; | ||
} | ||
else { | ||
programArgs.options[option.key] = option.default; | ||
validateParsedOptions_1.validateParsedOptions(parsedOptions, program); | ||
} | ||
return parsedOptions; | ||
} | ||
} | ||
if (config.arguments) { | ||
for (const argument of config.arguments) { | ||
const arg = programArgs.options[argument.key]; | ||
if (arg != null) { | ||
programArgs.options[argument.key] = | ||
arg != null && argument.parse | ||
? argument.multi | ||
? arg.map(argument.parse) | ||
: argument.parse(arg) | ||
: arg; | ||
catch (error) { | ||
if (error instanceof ParseError) { | ||
console.log(printError(error)); | ||
process.exit(2); | ||
} | ||
else if (argument.required) { | ||
throw new ParseError(`Missing required argument ${argument.key}.`, programArgs); | ||
} | ||
else { | ||
programArgs.options[argument.key] = argument.default; | ||
throw error; | ||
} | ||
} | ||
} | ||
if (programArgs.command) { | ||
const cmd = config.commandsByKey | ||
? config.commandsByKey[programArgs.command.cmd] | ||
: undefined; | ||
if (cmd == null) { | ||
throw new Error(`This should never happen.`); | ||
} | ||
validateProgramArgs(programArgs.command, cmd); | ||
} | ||
} | ||
function initConfig(config) { | ||
const program = Object.assign({}, config); | ||
let optionsByKey = {}; | ||
if (program.options) { | ||
program.optionsByKey = {}; | ||
for (const option of program.options) { | ||
if (option.key in program.optionsByKey) { | ||
throw new Error(`Option ${option.key} conflicts with another.`); | ||
} | ||
program.optionsByKey[option.key] = option; | ||
if (option.alias != null) { | ||
if (option.alias in program.optionsByKey) { | ||
throw new Error(`Option ${option.alias} conflicts with another.`); | ||
} | ||
program.optionsByKey[option.alias] = option; | ||
} | ||
} | ||
optionsByKey = Object.assign({}, program.optionsByKey); | ||
} | ||
if (program.arguments) { | ||
let required = true; | ||
let multi = false; | ||
for (const argument of program.arguments) { | ||
if (argument.key in optionsByKey) { | ||
throw new Error(`Argument ${argument.key} conflicts with another.`); | ||
} | ||
else { | ||
optionsByKey[argument.key] = argument; | ||
} | ||
if (required) { | ||
if (!argument.required) { | ||
required = false; | ||
} | ||
} | ||
else if (argument.required) { | ||
throw new Error(`Required argument ${argument.key} cannot follow an optional argument.`); | ||
} | ||
if (multi) { | ||
throw new Error(`Argument ${argument.key} cannot follow a multi-argument.`); | ||
} | ||
else { | ||
if (argument.multi) { | ||
multi = true; | ||
} | ||
} | ||
} | ||
} | ||
if (program.commands) { | ||
program.commandsByKey = {}; | ||
for (const command of program.commands) { | ||
if (program.commandsByKey[command.cmd] != null) { | ||
throw new Error(`Command ${command.cmd} conflicts with another.`); | ||
} | ||
program.commandsByKey[command.cmd] = initConfig(command); | ||
} | ||
} | ||
return program; | ||
} | ||
function createArgParser(config) { | ||
const program = initConfig(config); | ||
function parse(args) { | ||
@@ -269,66 +189,27 @@ const programArgs = parseArguments(args | ||
.reduce((acc, next) => acc.concat(next), []), program); | ||
validateProgramArgs(programArgs, program); | ||
validateParsedOptions_1.validateParsedOptions(programArgs, program); | ||
return programArgs; | ||
} | ||
function printError(error) { | ||
return `${error.message}\n\n${printUsage(error.program)}`; | ||
return `${error.message}\n\n${print_1.printProgram(error.program, program).usage}`; | ||
} | ||
function printUsage(programArgs) { | ||
let currentArgs = programArgs; | ||
let currentConfig = program; | ||
let lastConfig = currentConfig; | ||
const parts = []; | ||
while (currentArgs && currentConfig) { | ||
lastConfig = currentConfig; | ||
parts.push(lastConfig.options | ||
? `${currentArgs.cmd} [OPTIONS]` | ||
: currentArgs.cmd); | ||
currentConfig = | ||
currentConfig.commandsByKey && currentArgs.command | ||
? currentConfig.commandsByKey[currentArgs.command.cmd] | ||
: undefined; | ||
currentArgs = currentArgs.command; | ||
} | ||
let usage = parts.join(' '); | ||
let argumentStr; | ||
if (lastConfig.arguments) { | ||
const requiredArgs = lastConfig.arguments | ||
.filter(({ required }) => required) | ||
.map(({ key, multi }) => multi ? `<${key}>...` : `<${key}>`) | ||
.join(' '); | ||
const optionalArgs = lastConfig.arguments | ||
.filter(({ required }) => !required) | ||
.map(({ key, multi }) => multi ? `<${key}>...` : `<${key}>`) | ||
.join(' '); | ||
argumentStr = requiredArgs | ||
? optionalArgs | ||
? `${requiredArgs} [${optionalArgs}]` | ||
: requiredArgs | ||
: undefined; | ||
} | ||
const cmdStr = lastConfig.commandRequired | ||
? 'CMD' | ||
: '[CMD]'; | ||
usage = argumentStr | ||
? lastConfig.commands | ||
? `Usage:\n ${usage} ${argumentStr}\n OR\n ${usage} ${cmdStr}` | ||
: `Usage:\n ${usage} ${argumentStr}` | ||
: lastConfig.commands | ||
? `Usage:\n ${usage} ${cmdStr}` | ||
: `Usage:\n ${usage}`; | ||
usage = lastConfig.title | ||
? lastConfig.description | ||
? `${lastConfig.title}\n\n${lastConfig.description}\n\n${usage}` | ||
: `${lastConfig.title}\n\n${usage}` | ||
: lastConfig.description | ||
? `${lastConfig.description}\n\n${usage}` | ||
: usage; | ||
return usage; | ||
return print_1.printProgram(programArgs, program).usage; | ||
} | ||
function printHelp(programArgs) { | ||
return printUsage(programArgs); | ||
return print_1.printProgram(programArgs, program).help; | ||
} | ||
return { parse, printError, printUsage, printHelp }; | ||
function printVersion() { | ||
return program.version; | ||
} | ||
return { | ||
run, | ||
parse, | ||
printError, | ||
printUsage, | ||
printHelp, | ||
printVersion, | ||
}; | ||
} | ||
exports.createArgParser = createArgParser; | ||
//# sourceMappingURL=cmd-args.js.map |
{ | ||
"name": "cmd-args", | ||
"version": "2.0.0-alpha-3", | ||
"version": "2.0.0", | ||
"description": "A simple command-line argument parser for NodeJS command-line tools.", | ||
@@ -5,0 +5,0 @@ "main": "dist/cmd-args.js", |
@@ -25,2 +25,4 @@ # cmd-args | ||
description: 'my-cmd description...', | ||
help: true, | ||
version: 'v1.0.0', | ||
options: [ | ||
@@ -30,3 +32,3 @@ { | ||
key: 'verbose', | ||
alias: 'V', | ||
alias: 'v', | ||
description: 'Enable verbose mode.' | ||
@@ -51,3 +53,3 @@ }, | ||
var program = myParser.parse(process.argv.slice(2)); | ||
var program = myParser.run(); | ||
@@ -60,2 +62,43 @@ console.log(JSON.stringify(program), null, 2)) | ||
```bash | ||
$ my-cmd --version | ||
``` | ||
outputs: | ||
``` | ||
v1.0.0 | ||
``` | ||
example run: | ||
```bash | ||
$ my-cmd --help | ||
``` | ||
outputs: | ||
``` | ||
My CMD | ||
my-cmd description... | ||
Usage: | ||
my-cmd [OPTIONS] --help | ||
OR | ||
my-cmd [OPTIONS] <input-files>... | ||
my-cmd [OPTIONS]: | ||
--version Prints version of this command. | ||
--help Prints help text for this command. | ||
-v, --verbose Enable verbose mode. | ||
-o, --output-file[=ARG] Specifies location to write the output file. If not set the output will go to stdout. | ||
ARGUMENTS: | ||
input-files=ARG* List of input files to be used. | ||
``` | ||
example run: | ||
```bash | ||
$ my-cmd file1 file2 | ||
@@ -68,3 +111,3 @@ ``` | ||
{ | ||
"cmd": "my-cmd", | ||
"command": ["my-cmd"], | ||
"options": { | ||
@@ -87,3 +130,3 @@ "verbose": false, | ||
{ | ||
"cmd": "my-cmd", | ||
"command": ["my-cmd"], | ||
"options": { | ||
@@ -107,3 +150,3 @@ "verbose": false, | ||
{ | ||
"cmd": "my-cmd", | ||
"command": ["my-cmd"], | ||
"options": { | ||
@@ -127,3 +170,3 @@ "verbose": false, | ||
{ | ||
"cmd": "my-cmd", | ||
"command": ["my-cmd"], | ||
"options": { | ||
@@ -130,0 +173,0 @@ "verbose": true, |
@@ -1,50 +0,13 @@ | ||
interface Argument<V> { | ||
key: string | ||
description?: string | ||
multi?: boolean | ||
required?: boolean | ||
default?: V | ||
parse?: (val: string) => V | ||
} | ||
// tslint:disable: no-console | ||
import { | ||
Arg, | ||
ParsedOptions, | ||
CommandInternal, | ||
Program, | ||
ProgramInternal, | ||
} from './types' | ||
import { printProgram } from './print' | ||
import { initConfig } from './initConfig' | ||
import { validateParsedOptions } from './validateParsedOptions' | ||
interface Option<V> extends Argument<V> { | ||
type: 'option' | 'flag' | ||
alias?: string | ||
} | ||
interface Command { | ||
cmd: string | ||
title?: string | ||
description?: string | ||
example?: string | ||
help?: boolean | ||
version?: string | ||
options?: Option<any>[] | ||
commands?: Command[] | ||
commandRequired?: boolean | ||
arguments?: Argument<any>[] | ||
} | ||
interface Program extends Command { | ||
parseOnly?: boolean | ||
} | ||
interface CommandInternal extends Command { | ||
optionsByKey?: Record<string, Option<any>> | ||
commandsByKey?: Record<string, CommandInternal> | ||
} | ||
interface Arg { | ||
type: 'argument' | 'option' | 'flag' | ||
key?: string | ||
hasValue?: boolean | ||
value?: string | ||
} | ||
interface CommandArgs { | ||
cmd: string | ||
options: Record<string, any> | ||
command?: CommandArgs | ||
} | ||
const OPTION = /^\-\-([^=]+)(?:(=)(.*)?)?$/ | ||
@@ -86,3 +49,3 @@ const OPTION_ALIAS = /^\-([^=])(?:(=)(.*)?)?$/ | ||
next: Arg | undefined, | ||
program: CommandArgs | ||
program: ParsedOptions | ||
) { | ||
@@ -104,7 +67,7 @@ if (!arg.hasValue) { | ||
class ParseError extends Error { | ||
export class ParseError extends Error { | ||
name = 'ParseError' | ||
program: CommandArgs | ||
program: ParsedOptions | ||
constructor(message: string, program: CommandArgs) { | ||
constructor(message: string, program: ParsedOptions) { | ||
super(message) | ||
@@ -119,7 +82,6 @@ this.program = program | ||
// internal | ||
programArgs: CommandArgs = { | ||
cmd: config.cmd, | ||
parsedOptions: ParsedOptions = { | ||
command: [config.cmd], | ||
options: {}, | ||
}, | ||
commandArgs = programArgs, | ||
currentConfig = config, | ||
@@ -130,5 +92,5 @@ argumentIndex = 0, | ||
next = args[index + 1] | ||
): CommandArgs { | ||
): ParsedOptions { | ||
if (index >= args.length) { | ||
return programArgs | ||
return parsedOptions | ||
} | ||
@@ -146,27 +108,35 @@ | ||
`Flag ${arg.key} cannot specify a value.`, | ||
programArgs | ||
parsedOptions | ||
) | ||
} | ||
commandArgs.options[option.key] = true | ||
parsedOptions.options[option.key] = true | ||
} else if (arg.type === 'flag') { | ||
throw new ParseError( | ||
`Option ${option.key} cannot be used as a flag.`, | ||
programArgs | ||
parsedOptions | ||
) | ||
} else if (option.multi) { | ||
skipNext = getOptionValue(arg, next, programArgs) | ||
skipNext = getOptionValue( | ||
arg, | ||
next, | ||
parsedOptions | ||
) | ||
const array = (commandArgs.options[option.key] = | ||
commandArgs.options[option.key] || []) | ||
const array = (parsedOptions.options[option.key] = | ||
parsedOptions.options[option.key] || []) | ||
array.push(arg.value) | ||
} else { | ||
skipNext = getOptionValue(arg, next, programArgs) | ||
if (option.key in commandArgs.options) { | ||
skipNext = getOptionValue( | ||
arg, | ||
next, | ||
parsedOptions | ||
) | ||
if (option.key in parsedOptions.options) { | ||
throw new ParseError( | ||
`Option ${option.key} does not except multiple values.`, | ||
programArgs | ||
parsedOptions | ||
) | ||
} | ||
commandArgs.options[option.key] = arg.value | ||
parsedOptions.options[option.key] = arg.value | ||
} | ||
@@ -176,3 +146,3 @@ } else { | ||
`Unknown option ${arg.key}.`, | ||
programArgs | ||
parsedOptions | ||
) | ||
@@ -183,3 +153,3 @@ } | ||
`Unknown option ${arg.key}.`, | ||
programArgs | ||
parsedOptions | ||
) | ||
@@ -194,9 +164,5 @@ } | ||
if (command) { | ||
commandArgs.command = { | ||
cmd: command.cmd, | ||
options: {}, | ||
} | ||
parsedOptions.command.push(command.cmd) | ||
// tslint:disable: no-parameter-reassignment | ||
commandArgs = commandArgs.command | ||
currentConfig = command | ||
@@ -210,12 +176,12 @@ argumentIndex = 0 | ||
`Unexpected argument '${arg.value}'.`, | ||
programArgs | ||
parsedOptions | ||
) | ||
} | ||
if (argument.multi) { | ||
const array = (commandArgs.options[argument.key] = | ||
commandArgs.options[argument.key] || []) | ||
const array = (parsedOptions.options[argument.key] = | ||
parsedOptions.options[argument.key] || []) | ||
array.push(arg.value) | ||
} else { | ||
commandArgs.options[argument.key] = arg.value | ||
parsedOptions.options[argument.key] = arg.value | ||
argumentIndex += 1 | ||
@@ -226,3 +192,3 @@ } | ||
`Unexpected argument '${arg.value}'.`, | ||
programArgs | ||
parsedOptions | ||
) | ||
@@ -235,12 +201,12 @@ } | ||
`Unexpected argument '${arg.value}'.`, | ||
programArgs | ||
parsedOptions | ||
) | ||
} | ||
if (argument.multi) { | ||
const array = (commandArgs.options[argument.key] = | ||
commandArgs.options[argument.key] || []) | ||
const array = (parsedOptions.options[argument.key] = | ||
parsedOptions.options[argument.key] || []) | ||
array.push(arg.value) | ||
} else { | ||
commandArgs.options[argument.key] = arg.value | ||
parsedOptions.options[argument.key] = arg.value | ||
argumentIndex += 1 | ||
@@ -251,3 +217,3 @@ } | ||
`Unexpected argument '${arg.value}'.`, | ||
programArgs | ||
parsedOptions | ||
) | ||
@@ -260,4 +226,3 @@ } | ||
// internal | ||
programArgs, | ||
commandArgs, | ||
parsedOptions, | ||
currentConfig, | ||
@@ -269,147 +234,36 @@ argumentIndex, | ||
function validateProgramArgs( | ||
programArgs: CommandArgs, | ||
config: CommandInternal | ||
) { | ||
if (config.options) { | ||
for (const option of config.options) { | ||
const arg = programArgs.options[option.key] | ||
function createArgParser(config: Program) { | ||
const program = initConfig(config) as ProgramInternal | ||
if (arg != null) { | ||
programArgs.options[option.key] = option.parse | ||
? option.multi | ||
? arg.map(option.parse) | ||
: option.parse(arg) | ||
: arg | ||
} else if (option.required) { | ||
throw new ParseError( | ||
`Missing required option ${option.key}.`, | ||
programArgs | ||
) | ||
} else if (option.type === 'flag') { | ||
programArgs.options[option.key] = false | ||
} else { | ||
programArgs.options[option.key] = option.default | ||
} | ||
} | ||
} | ||
function run() { | ||
try { | ||
const args = process.argv.slice(2) | ||
const parsedOptions = parseArguments( | ||
args | ||
.map(parseArg) | ||
.reduce((acc, next) => acc.concat(next), []), | ||
program | ||
) | ||
if (config.arguments) { | ||
for (const argument of config.arguments) { | ||
const arg = programArgs.options[argument.key] | ||
if (arg != null) { | ||
programArgs.options[argument.key] = | ||
arg != null && argument.parse | ||
? argument.multi | ||
? arg.map(argument.parse) | ||
: argument.parse(arg) | ||
: arg | ||
} else if (argument.required) { | ||
throw new ParseError( | ||
`Missing required argument ${argument.key}.`, | ||
programArgs | ||
) | ||
if (parsedOptions.options.help) { | ||
console.log(printHelp(parsedOptions)) | ||
process.exit(0) | ||
} else if (parsedOptions.options.version) { | ||
console.log(printVersion()) | ||
process.exit(0) | ||
} else { | ||
programArgs.options[argument.key] = argument.default | ||
validateParsedOptions(parsedOptions, program) | ||
} | ||
} | ||
} | ||
if (programArgs.command) { | ||
const cmd = config.commandsByKey | ||
? config.commandsByKey[programArgs.command.cmd] | ||
: undefined | ||
if (cmd == null) { | ||
throw new Error(`This should never happen.`) | ||
} | ||
validateProgramArgs(programArgs.command, cmd) | ||
} | ||
} | ||
function initConfig(config: Command) { | ||
const program: CommandInternal = { ...config } | ||
let optionsByKey = {} | ||
if (program.options) { | ||
program.optionsByKey = {} | ||
for (const option of program.options) { | ||
if (option.key in program.optionsByKey) { | ||
throw new Error( | ||
`Option ${option.key} conflicts with another.` | ||
) | ||
} | ||
program.optionsByKey[option.key] = option | ||
if (option.alias != null) { | ||
if (option.alias in program.optionsByKey) { | ||
throw new Error( | ||
`Option ${option.alias} conflicts with another.` | ||
) | ||
} | ||
program.optionsByKey[option.alias] = option | ||
} | ||
} | ||
optionsByKey = { ...program.optionsByKey } | ||
} | ||
if (program.arguments) { | ||
let required = true | ||
let multi = false | ||
for (const argument of program.arguments) { | ||
if (argument.key in optionsByKey) { | ||
throw new Error( | ||
`Argument ${argument.key} conflicts with another.` | ||
) | ||
return parsedOptions | ||
} catch (error) { | ||
if (error instanceof ParseError) { | ||
console.log(printError(error)) | ||
process.exit(2) | ||
} else { | ||
optionsByKey[argument.key] = argument | ||
throw error | ||
} | ||
if (required) { | ||
if (!argument.required) { | ||
required = false | ||
} | ||
} else if (argument.required) { | ||
throw new Error( | ||
`Required argument ${argument.key} cannot follow an optional argument.` | ||
) | ||
} | ||
if (multi) { | ||
throw new Error( | ||
`Argument ${argument.key} cannot follow a multi-argument.` | ||
) | ||
} else { | ||
if (argument.multi) { | ||
multi = true | ||
} | ||
} | ||
} | ||
} | ||
if (program.commands) { | ||
program.commandsByKey = {} | ||
for (const command of program.commands) { | ||
if (program.commandsByKey[command.cmd] != null) { | ||
throw new Error( | ||
`Command ${command.cmd} conflicts with another.` | ||
) | ||
} | ||
program.commandsByKey[command.cmd] = initConfig( | ||
command | ||
) as CommandInternal | ||
} | ||
} | ||
return program | ||
} | ||
function createArgParser(config: Program) { | ||
const program = initConfig(config) | ||
function parse(args: string[]) { | ||
@@ -423,3 +277,3 @@ const programArgs = parseArguments( | ||
validateProgramArgs(programArgs, program) | ||
validateParsedOptions(programArgs, program) | ||
@@ -430,87 +284,29 @@ return programArgs | ||
function printError(error: ParseError) { | ||
return `${error.message}\n\n${printUsage( | ||
error.program | ||
)}` | ||
return `${error.message}\n\n${ | ||
printProgram(error.program, program).usage | ||
}` | ||
} | ||
function printUsage(programArgs: CommandArgs) { | ||
let currentArgs: CommandArgs | undefined = programArgs | ||
let currentConfig: CommandInternal | undefined = program | ||
let lastConfig: CommandInternal = currentConfig | ||
function printUsage(programArgs: ParsedOptions) { | ||
return printProgram(programArgs, program).usage | ||
} | ||
const parts: string[] = [] | ||
function printHelp(programArgs: ParsedOptions) { | ||
return printProgram(programArgs, program).help | ||
} | ||
while (currentArgs && currentConfig) { | ||
lastConfig = currentConfig | ||
parts.push( | ||
lastConfig.options | ||
? `${currentArgs.cmd} [OPTIONS]` | ||
: currentArgs.cmd | ||
) | ||
currentConfig = | ||
currentConfig.commandsByKey && currentArgs.command | ||
? currentConfig.commandsByKey[ | ||
currentArgs.command.cmd | ||
] | ||
: undefined | ||
currentArgs = currentArgs.command | ||
} | ||
let usage = parts.join(' ') | ||
let argumentStr: string | undefined | ||
if (lastConfig.arguments) { | ||
const requiredArgs = lastConfig.arguments | ||
.filter(({ required }) => required) | ||
.map(({ key, multi }) => | ||
multi ? `<${key}>...` : `<${key}>` | ||
) | ||
.join(' ') | ||
const optionalArgs = lastConfig.arguments | ||
.filter(({ required }) => !required) | ||
.map(({ key, multi }) => | ||
multi ? `<${key}>...` : `<${key}>` | ||
) | ||
.join(' ') | ||
argumentStr = requiredArgs | ||
? optionalArgs | ||
? `${requiredArgs} [${optionalArgs}]` | ||
: requiredArgs | ||
: undefined | ||
} | ||
const cmdStr = lastConfig.commandRequired | ||
? 'CMD' | ||
: '[CMD]' | ||
usage = argumentStr | ||
? lastConfig.commands | ||
? `Usage:\n ${usage} ${argumentStr}\n OR\n ${usage} ${cmdStr}` | ||
: `Usage:\n ${usage} ${argumentStr}` | ||
: lastConfig.commands | ||
? `Usage:\n ${usage} ${cmdStr}` | ||
: `Usage:\n ${usage}` | ||
usage = lastConfig.title | ||
? lastConfig.description | ||
? `${lastConfig.title}\n\n${lastConfig.description}\n\n${usage}` | ||
: `${lastConfig.title}\n\n${usage}` | ||
: lastConfig.description | ||
? `${lastConfig.description}\n\n${usage}` | ||
: usage | ||
return usage | ||
function printVersion() { | ||
return program.version | ||
} | ||
function printHelp(programArgs: CommandArgs) { | ||
return printUsage(programArgs) | ||
return { | ||
run, | ||
parse, | ||
printError, | ||
printUsage, | ||
printHelp, | ||
printVersion, | ||
} | ||
return { parse, printError, printUsage, printHelp } | ||
} | ||
export { createArgParser } |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
66512
28
1395
1
171
1
1