leo / args

Toolkit for building command line interfaces
https://npmjs.com/args
MIT License
461 stars 30 forks source link

Add basic support for arguments #127

Open G-Rath opened 6 years ago

G-Rath commented 6 years ago

Given #13 & args.sub, I think it's a reasonable next step for this library to provide some basic support for arguments.

I think it's relatively easy to do, given that once you're done parsing the options, args.sub gives you what's left. You could then do a new parse on that for arguments that are specified via an argument method, that takes similar parameters to the option method.

The tricky bit is in the return. For now I'd say just keep it as is; since we can get the values out of args.sub, and they're order dependent, I think it's reasonable to add this feature initially just for exiting if arguments are missing, and including arguments nicely in the help menu.

Following that, I'd consider adding an arguments property containing a map of the arguments, just to make it nicer to access using destructuring in a manner that's less order-dependent.

I'm happy & willing to take a crack at adding this myself over the next couple of days, if this is something you'd be happy to have added to the library.

Pointless code example:

const args = require('args');

const flags = args
    .option(
        'example',
        'the example option to play around with',
        ''
    )
    .parse(process.argv, {value: '<path>'});

console.log(flags);
console.log(args.sub);
console.log(process.argv);
PS > node ./scripts/args-example.js "./my/file/path" --example "hello world"

{ e: 'hello world', example: 'hello world' }
[ './my/file/path' ]
[ 'C:\\nodejs\\node.exe',
  'C:\\Users\\G-Rath\\workspace\\scripts\\args-example.js',
  './my/file/path',
  '--example',
  'hello world' ]
ntwcklng commented 6 years ago

Hey @G-Rath, can you give me a use case? I don't quite get it why we should add this feature.

G-Rath commented 6 years ago

I have a small project that 'manages' a JSON schema; mainly getting data from a source and validating it against that schema. As part of this, it runs 'migrations' to aid in making older outdated data compatible with newer versions of the schema before running it's validation.

As a result, to make things easier, I've created a 'create-migration' script:

/**
 * Script for creating a new migration file for the `DataMigrator`,
 * using `AbstractDataMigration`, in `./src/migrators/migrations/`.
 */

// ensure that process.env.INIT_CWD is set to *something*
require('./functions/ensureInitCwdEnvironmentValue')(__dirname);

const /** @type {module:path} */ path = require('path');
const /** @type {module:fs} */ fs = require('fs');

const args = require('args');

const flags = args
    .option(
        'message',
        'The deprecation warning message',
        ''
    )
    .option(
        'datetime',
        'The datetime this migration was created on',
        (new Date()).toISOString()
    )
    .option(
        'force',
        'Forcibly create the migration, even if it already exists',
        false
    )
    .parse(process.argv, { value: '<className>' });

const [className] = args.sub; // the first property should be the name

if (!className) {
    console.error('ERROR: the name property is required');
    process.exit(1);
}

// ... make the migration, write to file, etc etc

This script requires the name of the migration class as an argument, and has the following help output:

PS C:\Users\G-Rath\workspace\prefer-projects\bot-configs-manager> npm run create-migration help

> @zwerm/bot-configs-manager@2.0.0 create-migration C:\Users\G-Rath\workspace\prefer-projects\bot-configs-manager
> node ./scripts/npm-create-migration.js "help"

  Usage: npm create-migration.js [options] [command] <className>

  Commands:
    help     Display help
    version  Display version

  Options:
    -d, --datetime [value]  The datetime this migration was created on (defaults to "2018-06-21T21:16:32.421Z")
    -f, --force             Forcibly create the migration, even if it already exists (disabled by default)
    -h, --help              Output usage information
    -m, --message           The deprecation warning message (defaults to "")
    -v, --version           Output the version number

Now, I can add <className> via args.parse(process.argv, { value: '<className>' });, and (I discovered while formulating this reply) extend the usage text with the usageFilter property, but that requires a rather large of extra effort if I want to achieve the same look as the rest of the text, as well as having to find a way to nicely insert my arguments text block in between the commands and options blocks.

I also have to do

const [className] = args.sub; // the first property should be the name

if (!className) {
    console.error('ERROR: the name property is required');
    process.exit(1);
}

To make className actually required.

These two points are what args is already doing with options; Thus I think it would round this library off perfectly if it had an .argument equivalent of .option, that would basically be a slimmed down version (since it wouldn't have to handle things like short vs long versions, and trying to be order-independent).

Ultimately, I'd love to be able to do:

const flags = args
    .argument(
        'className',
        `The name of the migration class. Since this will also be used as the filename for the class, it shouldn't contain any file extensions, or invalid symbols.`
    )
    .option(
        'message',
        'The deprecation warning message',
        ''
    )
    .option(
        'datetime',
        'The datetime this migration was created on',
        (new Date()).toISOString()
    )
    .option(
        'force',
        'Forcibly create the migration, even if it already exists',
        false
    )
    .parse(process.argv);

which would result in something like this:

PS C:\Users\G-Rath\workspace\prefer-projects\bot-configs-manager> npm run create-migration help

> @zwerm/bot-configs-manager@2.0.0 create-migration C:\Users\G-Rath\workspace\prefer-projects\bot-configs-manager
> node ./scripts/npm-create-migration.js "help"

  Usage: npm create-migration.js [options] [command] <className>

  Commands:
    help     Display help
    version  Display version

  Arguments:
    className             The name of the migration class. Since this will also be used as the filename for the class, it shouldn't contain any file extensions, or invalid symbols.

  Options:
    -d, --datetime [value]  The datetime this migration was created on (defaults to "2018-06-21T21:16:32.421Z")
    -f, --force             Forcibly create the migration, even if it already exists (disabled by default)
    -h, --help              Output usage information
    -m, --message           The deprecation warning message (defaults to "")
    -v, --version           Output the version number