Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

commander

Package Overview
Dependencies
Maintainers
6
Versions
115
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

commander - npm Package Compare versions

Comparing version 8.3.0 to 9.0.0-0

6

lib/argument.js

@@ -93,3 +93,3 @@ const { InvalidArgumentError } = require('./error.js');

/**
* Only allow option value to be one of choices.
* Only allow argument value to be one of choices.
*

@@ -115,3 +115,3 @@ * @param {string[]} values

/**
* Make option-argument required.
* Make argument required.
*/

@@ -124,3 +124,3 @@ argRequired() {

/**
* Make option-argument optional.
* Make argument optional.
*/

@@ -127,0 +127,0 @@ argOptional() {

@@ -44,2 +44,3 @@ const EventEmitter = require('events').EventEmitter;

this._executableFile = null; // custom name for executable
this._executableDir = null; // custom search directory for subcommands
this._defaultCommandName = null;

@@ -56,3 +57,3 @@ this._exitCallback = null;

this._showHelpAfterError = false;
this._showSuggestionAfterError = false;
this._showSuggestionAfterError = true;

@@ -263,15 +264,6 @@ // see .configureOutput() for docs

addCommand(cmd, opts) {
if (!cmd._name) throw new Error('Command passed to .addCommand() must have a name');
// To keep things simple, block automatic name generation for deeply nested executables.
// Fail fast and detect when adding rather than later when parsing.
function checkExplicitNames(commandArray) {
commandArray.forEach((cmd) => {
if (cmd._executableHandler && !cmd._executableFile) {
throw new Error(`Must specify executableFile for deeply nested executable: ${cmd.name()}`);
}
checkExplicitNames(cmd.commands);
});
if (!cmd._name) {
throw new Error(`Command passed to .addCommand() must have a name
- specify the name in Command constructor or using .name()`);
}
checkExplicitNames(cmd.commands);

@@ -522,15 +514,11 @@ opts = opts || {};

let defaultValue = option.defaultValue;
// preassign default value for --no-*, [optional], <required>, or plain flag if boolean value
if (option.negate || option.optional || option.required || typeof defaultValue === 'boolean') {
// when --no-foo we make sure default is true, unless a --foo option is already defined
if (option.negate) {
const positiveLongFlag = option.long.replace(/^--no-/, '--');
defaultValue = this._findOption(positiveLongFlag) ? this.getOptionValue(name) : true;
// store default value
if (option.negate) {
// --no-foo is special and defaults foo to true, unless a --foo option is already defined
const positiveLongFlag = option.long.replace(/^--no-/, '--');
if (!this._findOption(positiveLongFlag)) {
this.setOptionValueWithSource(name, option.defaultValue === undefined ? true : option.defaultValue, 'default');
}
// preassign only if we have a default
if (defaultValue !== undefined) {
this.setOptionValueWithSource(name, defaultValue, 'default');
}
} else if (option.defaultValue !== undefined) {
this.setOptionValueWithSource(name, option.defaultValue, 'default');
}

@@ -543,9 +531,13 @@

const handleOptionValue = (val, invalidValueMessage, valueSource) => {
// Note: using closure to access lots of lexical scoped variables.
const oldValue = this.getOptionValue(name);
// val is null for optional option used without an optional-argument.
// val is undefined for boolean and negated option.
if (val == null && option.presetArg !== undefined) {
val = option.presetArg;
}
// custom processing
const oldValue = this.getOptionValue(name);
if (val !== null && option.parseArg) {
try {
val = option.parseArg(val, oldValue === undefined ? defaultValue : oldValue);
val = option.parseArg(val, oldValue);
} catch (err) {

@@ -562,14 +554,13 @@ if (err.code === 'commander.invalidArgument') {

// unassigned or boolean value
if (typeof oldValue === 'boolean' || typeof oldValue === 'undefined') {
// if no value, negate false, and we have a default, then use it!
if (val == null) {
this.setOptionValueWithSource(name, option.negate ? false : defaultValue || true, valueSource);
// Fill-in appropriate missing values. Long winded but easy to follow.
if (val == null) {
if (option.negate) {
val = false;
} else if (option.isBoolean() || option.optional) {
val = true;
} else {
this.setOptionValueWithSource(name, val, valueSource);
val = ''; // not normal, parseArg might have failed or be a mock function for testing
}
} else if (val !== null) {
// reassign
this.setOptionValueWithSource(name, option.negate ? false : val, valueSource);
}
this.setOptionValueWithSource(name, val, valueSource);
};

@@ -598,2 +589,5 @@

_optionEx(config, flags, description, fn, defaultValue) {
if (typeof flags === 'object' && flags instanceof Option) {
throw new Error('To add an Option object use addOption() instead of option() or requiredOption()');
}
const option = this.createOption(flags, description);

@@ -830,4 +824,4 @@ option.makeOptionMandatory(!!config.mandatory);

/**
* Get user arguments implied or explicit arguments.
* Side-effects: set _scriptPath if args included application, and use that to set implicit command name.
* Get user arguments from implied or explicit arguments.
* Side-effects: set _scriptPath if args included script. Used for default program name, and subcommand searches.
*

@@ -876,8 +870,6 @@ * @api private

}
if (!this._scriptPath && require.main) {
this._scriptPath = require.main.filename;
}
// Guess name, used in usage in help.
this._name = this._name || (this._scriptPath && path.basename(this._scriptPath, path.extname(this._scriptPath)));
// Find default name for program from arguments.
if (!this._name && this._scriptPath) this.nameFromFilename(this._scriptPath);
this._name = this._name || 'program';

@@ -948,44 +940,53 @@ return userArgs;

function findFile(baseDir, baseName) {
// Look for specified file
const localBin = path.resolve(baseDir, baseName);
if (fs.existsSync(localBin)) return localBin;
// Stop looking if candidate already has an expected extension.
if (sourceExt.includes(path.extname(baseName))) return undefined;
// Try all the extensions.
const foundExt = sourceExt.find(ext => fs.existsSync(`${localBin}${ext}`));
if (foundExt) return `${localBin}${foundExt}`;
return undefined;
}
// Not checking for help first. Unlikely to have mandatory and executable, and can't robustly test for help flags in external command.
this._checkForMissingMandatoryOptions();
// Want the entry script as the reference for command name and directory for searching for other files.
let scriptPath = this._scriptPath;
// Fallback in case not set, due to how Command created or called.
if (!scriptPath && require.main) {
scriptPath = require.main.filename;
// executableFile and executableDir might be full path, or just a name
let executableFile = subcommand._executableFile || `${this._name}-${subcommand._name}`;
let executableDir = this._executableDir || '';
if (this._scriptPath) {
let resolvedScriptPath; // resolve possible symlink for installed npm binary
try {
resolvedScriptPath = fs.realpathSync(this._scriptPath);
} catch (err) {
resolvedScriptPath = this._scriptPath;
}
executableDir = path.resolve(path.dirname(resolvedScriptPath), executableDir);
}
let baseDir;
try {
const resolvedLink = fs.realpathSync(scriptPath);
baseDir = path.dirname(resolvedLink);
} catch (e) {
baseDir = '.'; // dummy, probably not going to find executable!
}
// Look for a local file in preference to a command in PATH.
if (executableDir) {
let localFile = findFile(executableDir, executableFile);
// name of the subcommand, like `pm-install`
let bin = path.basename(scriptPath, path.extname(scriptPath)) + '-' + subcommand._name;
if (subcommand._executableFile) {
bin = subcommand._executableFile;
}
const localBin = path.join(baseDir, bin);
if (fs.existsSync(localBin)) {
// prefer local `./<bin>` to bin in the $PATH
bin = localBin;
} else {
// Look for source files.
sourceExt.forEach((ext) => {
if (fs.existsSync(`${localBin}${ext}`)) {
bin = `${localBin}${ext}`;
// Legacy search using prefix of script name instead of command name
if (!localFile && !subcommand._executableFile && this._scriptPath) {
const legacyName = path.basename(this._scriptPath, path.extname(this._scriptPath));
if (legacyName !== this._name) {
localFile = findFile(executableDir, `${legacyName}-${subcommand._name}`);
}
});
}
executableFile = localFile || executableFile;
}
launchWithNode = sourceExt.includes(path.extname(bin));
launchWithNode = sourceExt.includes(path.extname(executableFile));
let proc;
if (process.platform !== 'win32') {
if (launchWithNode) {
args.unshift(bin);
args.unshift(executableFile);
// add executable arguments to spawn

@@ -996,6 +997,6 @@ args = incrementNodeInspectorPort(process.execArgv).concat(args);

} else {
proc = childProcess.spawn(bin, args, { stdio: 'inherit' });
proc = childProcess.spawn(executableFile, args, { stdio: 'inherit' });
}
} else {
args.unshift(bin);
args.unshift(executableFile);
// add executable arguments to spawn

@@ -1006,11 +1007,13 @@ args = incrementNodeInspectorPort(process.execArgv).concat(args);

const signals = ['SIGUSR1', 'SIGUSR2', 'SIGTERM', 'SIGINT', 'SIGHUP'];
signals.forEach((signal) => {
// @ts-ignore
process.on(signal, () => {
if (proc.killed === false && proc.exitCode === null) {
proc.kill(signal);
}
if (!proc.killed) { // testing mainly to avoid leak warnings during unit tests with mocked spawn
const signals = ['SIGUSR1', 'SIGUSR2', 'SIGTERM', 'SIGINT', 'SIGHUP'];
signals.forEach((signal) => {
// @ts-ignore
process.on(signal, () => {
if (proc.killed === false && proc.exitCode === null) {
proc.kill(signal);
}
});
});
});
}

@@ -1030,9 +1033,13 @@ // By default terminate process when spawned process terminates.

if (err.code === 'ENOENT') {
const executableMissing = `'${bin}' does not exist
const executableDirMessage = executableDir
? `searched for local subcommand relative to directory '${executableDir}'`
: 'no directory for search for local subcommand, use .executableDir() to supply a custom directory';
const executableMissing = `'${executableFile}' does not exist
- if '${subcommand._name}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
- if the default executable name is not suitable, use the executableFile option to supply a custom name`;
- if the default executable name is not suitable, use the executableFile option to supply a custom name or path
- ${executableDirMessage}`;
throw new Error(executableMissing);
// @ts-ignore
} else if (err.code === 'EACCES') {
throw new Error(`'${bin}' not executable`);
throw new Error(`'${executableFile}' not executable`);
}

@@ -1241,3 +1248,7 @@ if (!exitCallback) {

actionResult = this._chainOrCall(actionResult, () => this._actionHandler(this.processedArgs));
if (this.parent) this.parent.emit(commandEvent, operands, unknown); // legacy
if (this.parent) {
actionResult = this._chainOrCall(actionResult, () => {
this.parent.emit(commandEvent, operands, unknown); // legacy
});
}
actionResult = this._chainOrCallHooks(actionResult, 'postAction');

@@ -1726,3 +1737,3 @@ return actionResult;

/**
* Get or set the name of the command
* Get or set the name of the command.
*

@@ -1740,2 +1751,39 @@ * @param {string} [str]

/**
* Set the name of the command from script filename, such as process.argv[1],
* or require.main.filename, or __filename.
*
* (Used internally and public although not documented in README.)
*
* @example
* program.nameFromFilename(require.main.filename);
*
* @param {string} filename
* @return {Command}
*/
nameFromFilename(filename) {
this._name = path.basename(filename, path.extname(filename));
return this;
}
/**
* Get or set the directory for searching for executable subcommands of this command.
*
* @example
* program.executableDir(__dirname);
* // or
* program.executableDir('subcommands');
*
* @param {string} [path]
* @return {string|Command}
*/
executableDir(path) {
if (path === undefined) return this._executableDir;
this._executableDir = path;
return this;
};
/**
* Return program help documentation.

@@ -1742,0 +1790,0 @@ *

@@ -238,5 +238,4 @@ const { humanReadableArgName } = require('./argument.js');

const extraInfo = [];
// Some of these do not make sense for negated boolean and suppress for backwards compatibility.
if (option.argChoices && !option.negate) {
if (option.argChoices) {
extraInfo.push(

@@ -246,5 +245,15 @@ // use stringify to match the display of the default value

}
if (option.defaultValue !== undefined && !option.negate) {
extraInfo.push(`default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`);
if (option.defaultValue !== undefined) {
// default for boolean and negated more for programmer than end user,
// but show true/false for boolean option as may be for hand-rolled env or config processing.
const showDefault = option.required || option.optional ||
(option.isBoolean() && typeof option.defaultValue === 'boolean');
if (showDefault) {
extraInfo.push(`default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`);
}
}
// preset for boolean and negated are more for programmer than end user
if (option.presetArg !== undefined && option.optional) {
extraInfo.push(`preset: ${JSON.stringify(option.presetArg)}`);
}
if (option.envVar !== undefined) {

@@ -251,0 +260,0 @@ extraInfo.push(`env: ${option.envVar}`);

@@ -31,2 +31,3 @@ const { InvalidArgumentError } = require('./error.js');

this.defaultValueDescription = undefined;
this.presetArg = undefined;
this.envVar = undefined;

@@ -53,2 +54,19 @@ this.parseArg = undefined;

/**
* Preset to use when option used without option-argument, especially optional but also boolean and negated.
* The custom processing (parseArg) is called.
*
* @example
* new Option('--color').default('GREYSCALE').preset('RGB');
* new Option('--donate [amount]').preset('20').argParser(parseFloat);
*
* @param {any} arg
* @return {Option}
*/
preset(arg) {
this.presetArg = arg;
return this;
};
/**
* Set environment variable to check for option value.

@@ -171,2 +189,15 @@ * Priority order of option values is default < env < cli

};
/**
* Return whether a boolean option.
*
* Options are one of boolean, negated, required argument, or optional argument.
*
* @return {boolean}
* @api private
*/
isBoolean() {
return !this.required && !this.optional && !this.negate;
};
}

@@ -173,0 +204,0 @@

{
"name": "commander",
"version": "8.3.0",
"version": "9.0.0-0",
"description": "the complete solution for node.js command-line programs",

@@ -30,3 +30,2 @@ "keywords": [

},
"main": "./index.js",
"files": [

@@ -40,6 +39,13 @@ "index.js",

"type": "commonjs",
"dependencies": {},
"main": "./index.js",
"exports": {
".": {
"require": "./index.js",
"import": "./esm.mjs"
},
"./esm.mjs": "./esm.mjs"
},
"devDependencies": {
"@types/jest": "^26.0.23",
"@types/node": "^14.17.3",
"@types/jest": "^27.0.3",
"@types/node": "^16.11.15",
"@typescript-eslint/eslint-plugin": "^4.27.0",

@@ -68,5 +74,5 @@ "@typescript-eslint/parser": "^4.27.0",

"engines": {
"node": ">= 12"
"node": "^12.20.0 || >=14"
},
"support": true
}

@@ -35,3 +35,4 @@ # Commander.js

- [Display help from code](#display-help-from-code)
- [.usage and .name](#usage-and-name)
- [.name](#name)
- [.usage](#usage)
- [.helpOption(flags, description)](#helpoptionflags-description)

@@ -69,4 +70,4 @@ - [.addHelpCommand()](#addhelpcommand)

```js
// CommonJS (.cjs)
const { program } = require('commander');
program.version('0.0.1');
```

@@ -77,19 +78,15 @@

```js
// CommonJS (.cjs)
const { Command } = require('commander');
const program = new Command();
program.version('0.0.1');
```
For named imports in ECMAScript modules, import from `commander/esm.mjs`.
```js
// index.mjs
import { Command } from 'commander/esm.mjs';
// ECMAScript (.mjs)
import { Command } from 'commander';
const program = new Command();
```
And in TypeScript:
```ts
// index.ts
// TypeScript (.ts)
import { Command } from 'commander';

@@ -155,3 +152,3 @@ const program = new Command();

You can specify a default value for an option which takes a value.
You can specify a default value for an option.

@@ -182,3 +179,3 @@ Example file: [options-defaults.js](./examples/options-defaults.js)

If you define `--foo` first, adding `--no-foo` does not change the default value from what it would
otherwise be. You can specify a default boolean value for a boolean option and it can be overridden on command line.
otherwise be.

@@ -323,3 +320,4 @@ Example file: [options-negatable.js](./examples/options-negatable.js)

.addOption(new Option('-d, --drink <size>', 'drink size').choices(['small', 'medium', 'large']))
.addOption(new Option('-p, --port <number>', 'port number').env('PORT'));
.addOption(new Option('-p, --port <number>', 'port number').env('PORT'))
.addOption(new Option('--donate [amount]', 'optional donation in dollars').preset('20').argParser(parseFloat));
```

@@ -335,2 +333,3 @@

-p, --port <number> port number (env: PORT)
--donate [amount] optional donation in dollars (preset: 20)
-h, --help display help for command

@@ -341,4 +340,4 @@

$ PORT=80 extra
Options: { timeout: 60, port: '80' }
$ PORT=80 extra --donate
Options: { timeout: 60, donate: 20, port: '80' }
```

@@ -566,4 +565,5 @@

When `.command()` is invoked with a description argument, this tells Commander that you're going to use stand-alone executables for subcommands.
Commander will search the executables in the directory of the entry script (like `./examples/pm`) with the name `program-subcommand`, like `pm-install`, `pm-search`.
You can specify a custom name with the `executableFile` configuration option.
Commander will search the files in the directory of the entry script for a file with the name combination `command-subcommand`, like `pm-install` or `pm-search` in the example below. The search includes trying common file extensions, like `.js`.
You may specify a custom name (and path) with the `executableFile` configuration option.
You may specify a custom search directory for subcommands with `.executableDir()`.

@@ -576,2 +576,3 @@ You handle the options for an executable (sub)command in the executable, and don't declare them at the top-level.

program
.name('pm')
.version('0.1.0')

@@ -726,8 +727,23 @@ .command('install [name]', 'install one or more packages')

### .usage and .name
### .name
These allow you to customise the usage description in the first line of the help. The name is otherwise
deduced from the (full) program arguments. Given:
The command name appears in the help, and is also used for locating stand-alone executable subcommands.
You may specify the program name using `.name()` or in the Command constructor. For the program, Commander will
fallback to using the script name from the full arguments passed into `.parse()`. However, the script name varies
depending on how your program is launched so you may wish to specify it explicitly.
```js
program.name('pizza');
const pm = new Command('pm');
```
Subcommands get a name when specified using `.command()`. If you create the subcommand yourself to use with `.addCommand()`,
then set the name using `.name()` or in the Command constructor.
### .usage
This allows you to customise the usage description in the first line of the help. Given:
```js
program

@@ -746,3 +762,3 @@ .name("my-command")

By default every command has a help option. Override the default help flags and description. Pass false to disable the built-in help option.
By default every command has a help option. You may change the default help flags and description. Pass false to disable the built-in help option.

@@ -984,2 +1000,3 @@ ```js

program
.name('deploy')
.version('0.0.1')

@@ -1019,4 +1036,4 @@ .option('-c, --config <path>', 'set config path', './deploy.conf');

The current version of Commander is fully supported on Long Term Support versions of node, and requires at least node v12.
(For older versions of node, use an older version of Commander. Commander version 2.x has the widest support.)
The current version of Commander is fully supported on Long Term Support versions of Node.js, and requires at least v12.20.0.
(For older versions of Node.js, use an older version of Commander.)

@@ -1023,0 +1040,0 @@ The main forum for free and community support is the project [Issues](https://github.com/tj/commander.js/issues) on GitHub.

@@ -67,3 +67,3 @@ // Type definitions for commander

/**
* Make option-argument required.
* Make argument required.
*/

@@ -73,3 +73,3 @@ argRequired(): this;

/**
* Make option-argument optional.
* Make argument optional.
*/

@@ -105,2 +105,14 @@ argOptional(): this;

/**
* Preset to use when option used without option-argument, especially optional but also boolean and negated.
* The custom processing (parseArg) is called.
*
* @example
* ```ts
* new Option('--color').default('GREYSCALE').preset('RGB');
* new Option('--donate [amount]').preset('20').argParser(parseFloat);
* ```
*/
preset(arg: unknown): this;
/**
* Set environment variable to check for option value.

@@ -146,2 +158,10 @@ * Priority order of option values is default < env < cli

attributeName(): string;
/**
* Return whether a boolean option.
*
* Options are one of boolean, negated, required argument, or optional argument.
*/
isBoolean(): boolean;
}

@@ -714,2 +734,35 @@

/**
* Set the name of the command from script filename, such as process.argv[1],
* or require.main.filename, or __filename.
*
* (Used internally and public although not documented in README.)
*
* @example
* ```ts
* program.nameFromFilename(require.main.filename);
* ```
*
* @returns `this` command for chaining
*/
nameFromFilename(filename: string): this;
/**
* Set the directory for searching for executable subcommands of this command.
*
* @example
* ```ts
* program.executableDir(__dirname);
* // or
* program.executableDir('subcommands');
* ```
*
* @returns `this` command for chaining
*/
executableDir(path: string): this;
/**
* Get the executable search directory.
*/
executableDir(): string;
/**
* Output help information for this command.

@@ -716,0 +769,0 @@ *

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