Skip to content
This repository has been archived by the owner on Apr 5, 2023. It is now read-only.

Latest commit

 

History

History
280 lines (220 loc) · 9.58 KB

README.md

File metadata and controls

280 lines (220 loc) · 9.58 KB

An advanced command line interface (CLI) generator for NodeJs

An efficient, object centric auto generated command line interface.

Package includes:

  1. Cli engine, and supporting classes.
  2. Internal logger with contexts, common timestamp generation and a colors.
  3. VSCode snippets
  4. Load and save configuration from/to file.

BETA

Install

npm install lamaani/infer

TL;DR

To define a simple cli,

const {Cli} = require('@lamaani/infer')
const cli = new Cli({name: 'my_cli'})

// Add other commands
cli.default(
  async ({arg = null, flag = false}) => {
    console.info('Recived argument: ' + arg)
    console.info('Flag is: ' + flag)
  },
  {
    arg: {
      type: 'named',
      aliases: ['a'],
    },
    flag: {
      type: 'flag',
    },
  }
)

if (require.main == module)
  cli.parse().catch((err) => {
    console.error(err)
    process.exit(err.code || 1)
  })

With cascading commands,

const Cli = require('@lamaani/infer').Cli
const cli = new Cli({name: 'myapp'})
const myrunner = new MyRunner()

// optional sub menu:
cli.set('run', {}, {description: 'stuff to run'})

// Using a class to define the arguments.
// the command: myapp run class
cli.set('run class', myrunner, {
  description: 'Get options from a class',
  action: (options) => myrunner.run(options),
})

// Directly define the arguments.
cli.set(
  'run special',
  {
    arg: {
      type: 'named',
      aliases: ['a'],
    },
    flag: {
      type: 'flag',
    },
  },
  {
    description: 'run an anonymous action.',
    action: (options) => console.log(JSON.stringify(options, null, 2)),
  }
)

if (require.main == module)
  cli.parse().catch((err) => {
    console.error(err)
    process.exit(err.code || 1)
  })

Where MyRunner is,

/** @typedef {import('lamaani/infer').CliArgument} CliArgument */

class MyRunner {
  constructor() {
    // arguments that start with __$
    // are cli arguments, and will
    // be set to this object when parsed.

    /** Yet another argument */
    this.arg = 'default value'
    /** @type {CliArgument} */
    this.__$arg = {
      type: 'named',
      enviromentVariable: 'IFR_ARG',
      default: this.arg,
      description: 'Yet another argument',
    }
  }

  run(options) {}
}

To show help,

my_app --help

Commands

In infer, all commands are constructed as sentences, an example for a simple command would be,

myapp run special case positional_1 --flag --arg val positional_2

This above command would be translated to,

command_name='myapp run special case'
command_options={
  positional: ['positional_1','positional_2']
  named: {
    flag: true,
    arg: 'val'
  }
}

Named arguments or flags are not position dependent, therefore, the following is equivalent to the above,

myapp --flag run special --arg val case positional_1 positional_2

Limitations

  1. There are no combined flags, i.e. no -wWyDt.
  2. Arguments are parsed only as,
    • Single letters: a -> -a
    • Multi letters: arg -> --arg
  3. Positional arguments are only allowed for the final command, otherwise all positional arguments are parsed as commands.

Arguments

Argument types:

  1. named - a regular named argument. Any value. --arg, -a
  2. flag - A named argument, but no value (true if exists, in config you must set the value). --flag, -f
  3. positional - An argument that follows the command. Will be added to an array.
  4. env - An argument that would not appear in the CLI, but rather is only loaded from the environment variable. (Can be assigned in config)
  5. overflow - Catch all for any argument after the positional arguments. Allows unknown number of input arguments.
  6. transfer - Catch any argument after the symbol --. This allows separation of command arguments from arguments that need transferring to another command.
Name description possible values default
type The argument type ['named', 'flag', 'positional', 'env', 'overflow', 'transfer'] named
field_name The name of the field to update on the parent object [any] The field name in the object
match The name of the argument to match if named or flag [any] the field name, where any non letter or number is replaced with -
default the default value. [any] [null]
environmentVariable The matching environment variable. If exits, will be taken instead of default. The env in nodejs will be updated to match the field value. [string] If env then the field_name, otherwise null
aliases Array, the possible command aliases string[] []
description The argument description. [string] null
parse Method: (val)=>[any]. Called before any assignment. [function] null
doNotAssignToParent If true, dose not assign the argument to its parent. true/false false
doNotAssignToEnv If true, dose not assign the argument to the environment variable. true/false false
canBeStored If true, allows this argument to be stored to disk (will appear in the configuration file) true/false true

File configuration manager

To add a file configuration manager,

const cli = new (require('infer').Cli)()
const fs = require('fs')
const CliConfigCommand = require('zlib-cli/CliConfigCommand')

new CliConfigCommand(
  Cli,
  // command to load the configuration as a javascript object.
  () => require('./my_config.json'),
  // command to save the configuration to file.
  (config) => {
    fs.writeFileSync('./my_config.json', JSON.stringify(config))
  }
)

Then,

# to list
myapp config list
# myapp config get [command with dots] [named/flag]
myapp config get run.special.case arg
$> 48
# myapp config set [command with dots] [named/flag] [val/empty to remove]
myapp config set run.special.case arg 42

Logger

const Logger = require('infer').Logger

const logger = new Logger()
const sub_logger = logger.create('sub')
const sub_sub_logger = logger.create('sub-sub')

logger.level = 0
console.log('Normal log')
console.log()
logger.debug('lama')
logger.info('lama')
logger.warn('lama')
logger.error('lama')
logger.fatal('lama')
logger.trace('lama')

console.log()
console.log('With log symbols')
console.log()

logger.debug('lama', '=>')
logger.info('lama', '=>')
logger.warn('lama', '=>')
logger.error('lama', '=>')
logger.fatal('lama', '=>')
logger.trace('lama', '*')

console.log()
console.log('Sub loggers')
console.log()

sub_sub_logger.info('lama-sub-sub')
sub_logger.info('lama-sub')
logger.info('lama-parent')

Example

alt text

Contribution

Are welcome, please post issues or PR's if needed.

Implementations still missing:

Add an issue (or better submit PR) if you need these.

  1. XCom
  2. Examples (other than TL;DR)

Licence

Copyright © Zav Shotan and other contributors. It is free software, released under the MIT licence, and may be redistributed under the terms specified in LICENSE.