Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: CommandStrict #1404

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 26 additions & 6 deletions Readme.md
Expand Up @@ -35,7 +35,7 @@ Read this in other languages: English | [简体中文](./Readme_zh-CN.md)
- [Custom event listeners](#custom-event-listeners)
- [Bits and pieces](#bits-and-pieces)
- [.parse() and .parseAsync()](#parse-and-parseasync)
- [Avoiding option name clashes](#avoiding-option-name-clashes)
- [Strict](#strict)
- [TypeScript](#typescript)
- [createCommand()](#createcommand)
- [Import into ECMAScript Module](#import-into-ecmascript-module)
Expand Down Expand Up @@ -73,6 +73,14 @@ const program = new Command();
program.version('0.0.1');
```

Or even better for new programs, create a CommandStrict object which favors safe configuration over backwards compatibility.

```js
const { CommandStrict } = require('commander');
const program = new CommandStrict();
program.version('0.0.1');
```

## Options

Options are defined with the `.option()` method, also serving as documentation for the options. Each option can have a short flag (single character) and a long name, separated by a comma or space or vertical bar ('|').
Expand Down Expand Up @@ -688,26 +696,38 @@ program.parse(); // Implicit, and auto-detect electron
program.parse(['-f', 'filename'], { from: 'user' });
```

### Avoiding option name clashes
### Strict

The original and default behaviour is that the option values are stored
as properties on the program, and the action handler is passed a
The default configuration of the Command class changes slowly to preserve backwards compatibility.
The CommandStrict class uses latest recommended settings and is suggested
for new programs. All of the examples in this README and the examples work with both `Commnand` and
`CommandStrict`.

**Avoiding option name clashes**

The original and default `Command` behaviour is that the option values are stored
as properties on the command, and the action handler is passed a
command object with the options values stored as properties.
This is very convenient to code, but the downside is possible clashes with
existing properties of Command.
existing properties of `Command`.

There are two new routines to change the behaviour, and the default behaviour may change in the future:
There are two new routines to change the behaviour:

- `storeOptionsAsProperties`: whether to store option values as properties on command object, or store separately (specify false) and access using `.opts()`
- `passCommandToAction`: whether to pass command to action handler,
or just the options (specify false)

The suggested manual use is set them both the same. `Command` sets both to true, while `CommandStrict` sets both to false.

Example file: [storeOptionsAsProperties-action.js](./examples/storeOptionsAsProperties-action.js)

```js
const program = new Command();

program
.storeOptionsAsProperties(false)
.passCommandToAction(false);
// Now same settings as CommandStrict.

program
.name('my-program-name')
Expand Down
1 change: 1 addition & 0 deletions examples/storeOptionsAsProperties-action.js
Expand Up @@ -23,6 +23,7 @@ const program = new commander.Command();
program
.storeOptionsAsProperties(false) // <--- change behaviour
.passCommandToAction(false); // <--- change behaviour
// Now same settings as CommandStrict.

program
.name('my-program-name')
Expand Down
39 changes: 0 additions & 39 deletions examples/storeOptionsAsProperties-opts.js

This file was deleted.

19 changes: 19 additions & 0 deletions index.js
Expand Up @@ -2038,6 +2038,24 @@ Expecting one of '${allowedValues.join("', '")}'`);
}
};

class CommandStrict extends Command {
/**
* Initialize a new `CommandStrict` object, which adopts new patterns and best practices
* more aggressively than Command.
*
* @param {string} [name]
*/
constructor(name) {
super(name);
this.storeOptionsAsProperties(false);
this.passCommandToAction(false);
}

createCommand(name) {
return new CommandStrict(name);
};
}

/**
* Expose the root command.
*/
Expand All @@ -2050,6 +2068,7 @@ exports.program = exports; // More explicit access to global command.
*/

exports.Command = Command;
exports.CommandStrict = CommandStrict;
exports.Option = Option;
exports.CommanderError = CommanderError;
exports.InvalidOptionArgumentError = InvalidOptionArgumentError;
Expand Down
26 changes: 26 additions & 0 deletions tests/command.strict.test.js
@@ -0,0 +1,26 @@
const commander = require('../');

/* eslint jest/expect-expect: ["error", { "assertFunctionNames": ["expect", "expectStrictSettings"] }] */

function expectStrictSettings(command) {
// Use internal knowledge of the expected settings
expect(command._storeOptionsAsProperties).toEqual(false);
expect(command._passCommandToAction).toEqual(false);
}

test('when new CommandStrict then settings are strict', () => {
const program = new commander.CommandStrict();
expectStrictSettings(program);
});

test('when new CommandStrict creates command directly then settings are strict', () => {
const program = new commander.CommandStrict();
const subcommand = program.createCommand();
expectStrictSettings(subcommand);
});

test('when new CommandStrict creates command indirectly then settings are strict', () => {
const program = new commander.CommandStrict();
const subcommand = program.command('sub');
expectStrictSettings(subcommand);
});