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

Implicit program "name", deep dive #1569

Closed
shadowspawn opened this issue Jul 16, 2021 · 3 comments
Closed

Implicit program "name", deep dive #1569

shadowspawn opened this issue Jul 16, 2021 · 3 comments
Assignees

Comments

@shadowspawn
Copy link
Collaborator

shadowspawn commented Jul 16, 2021

The user can set the program name. If they don't, Commander has a go at finding a name to use. This turns out to be quite hard and less consistent than you might expect! (In practice a user will only be using a subset of all the possibilities, but still some potential for getting caught out.)

constructor

const program = new Command('constructor');
program.parse();
% node example -h
Usage: constructor [options]

.name()

const program = new Command();
program.name('name');
program.parse();
% node example -h
Usage: name [options]

Implicit, from args

A commonly used method for command-line scripts to find the name of the currently running scripts is to examine the passed arguments. The Node.js documentation for process.argv says:

The process.argv property returns an array containing the command-line arguments passed when the Node.js process was launched. The first element will be process.execPath. See process.argv0 if access to the original value of argv[0] is needed. The second element will be the path to the JavaScript file being executed. The remaining elements will be any additional command-line arguments.

Commander uses the parsed arguments to determine the default name if they (should) include the script name. The problem is there are so many ways to launch a node application and not all pass the script name, and platform differences as well.

#!/usr/local/bin/node
const program = new Command();
program.parse(process.args);

Passing script directly:

% node script.js -h
Usage: script [options]

Running script as an executable:

$ ./executable-file -h
Usage: executable-file [options]

For running via package.json in directory package-dir:

  "main": "main-file.js",
  "bin": {
    "bin-executable": "executable-file.js"
  },

Running a package returns the name of the package directory (not the script name):

% node . -h
Usage: package-dir [options]

Running an installed binary on Mac and Linux gets the bin name (not the script name):

% npm link
...
% bin-executable -h
Usage: bin-executable [options]

Running an installed binary on Windows gets the script name (not the bin name):

> npm link
...
> bin-executable -h
Usage: executable-file [options]

Implicit, not from args

Since Commander v5, we support parsing arguments which do not include the script name. In this case we fallback to using the main module, from Commander 8 using require.main.filename. This does give consistent behaviour for all the launch methods with CommonJs.

const program = new Command();
program.parse(process.argv.slice(2), { from: 'user' });
% node script.js -h
Usage: script [options]
% ./executable-file -h
Usage: executable-file [options]
% node . -h
Usage: main-file [options]
% npm link
...
% bin-executable -h
Usage: executable-file [options]

There is a defect I discovered though, not available when main script is ECMAScript!

% node script.mjs -h
Usage: null [options]

Summary

In Commander v8 the default program name may turn out to be the script name, the package directory name, the package.json "bin" name, or even null. The default program name from the arguments is different for an npm installed binary on Windows and non-Windows.

An additional caveat is that default program name derived from the arguments may not be sensible when running unit tests or other launches when the normal entry script is not in the launch arguments. And the non-arguments approach uses some Node.js state which may not be defined by other applications (e.g. iisnode).

The safe approach is of course to set the name yourself! So Commander does not have to solve all the problems.

@shadowspawn shadowspawn self-assigned this Jul 16, 2021
@shadowspawn
Copy link
Collaborator Author

Why care so much about the name, which is arguably just cosmetic?
We also use the full path of the main module to find stand-alone executable subcommands. See #714.

@shadowspawn
Copy link
Collaborator Author

shadowspawn commented Aug 4, 2021

The open issue that prompted this research is #714
The PR inspired by this issue is #1571

Closing this issue as having captured existing Commander behaviour, and investigated what is used elsewhere.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant