Git Product home page Git Product logo

sywac's Introduction

sywac

So you want a CLI...

Build Status Coverage Status JavaScript Style Guide Dependabot Badge

A better CLI framework, made for the ES2015 era.

Visit https://sywac.io for detailed documentation. NOTE! The docs site is still under construction.

Feature Highlights

  • Single package install
  • Asynchronous parsing, validation, and command execution
  • Type-based argument parsing
  • Plug in your own types or override/extend the built-in ones
  • Support for simple CLIs or complex nested command trees
  • First-class support for positional arguments, with or without commands
  • Flexible auto-generated help content
  • Support for ANSI styles/colors (we recommend chalk)
  • Define styles/colors inline or decorate content with style hooks
  • Coherent API
  • Parse strings as easily as process.argv
  • Supports concurrent parsing, safe for chatbots or other server-side apps

Quick Start Guide

First install sywac from npm:

$ npm install --save sywac

Then create a cli.js file with code similar to this:

#!/usr/bin/env node

const cli = require('sywac')
  .positional('<string>', { paramsDesc: 'A required string argument' })
  .boolean('-b, --bool', { desc: 'A boolean option' })
  .number('-n, --num <number>', { desc: 'A number option' })
  .help('-h, --help')
  .version('-v, --version')
  .showHelpByDefault()
  .outputSettings({ maxWidth: 75 })

module.exports = cli

async function main () {
  const argv = await cli.parseAndExit()
  console.log(JSON.stringify(argv, null, 2))
}

if (require.main === module) main()

Make the cli.js file executable:

$ chmod +x cli.js

And set up cli.js as the "bin" field in package.json:

{
  "name": "example",
  "version": "0.1.0",
  "bin": "cli.js"
}

Tip:

You can use npm init sywac to easily set up the above and add sywac to your project.

Then test it out. Without any arguments, it will print the help text.

$ ./cli.js
Usage: cli <string> [options]

Arguments:
  <string>  A required string argument                  [required] [string]

Options:
  -b, --bool          A boolean option                            [boolean]
  -n, --num <number>  A number option                              [number]
  -h, --help          Show help                  [commands: help] [boolean]
  -v, --version       Show version number     [commands: version] [boolean]

Let's try passing some arguments:

$ ./cli.js hello -b -n 42
{
  "_": [],
  "string": "hello",
  "b": true,
  "bool": true,
  "n": 42,
  "num": 42,
  "h": false,
  "help": false,
  "v": false,
  "version": false
}

What happens if we pass flags without a string argument?

$ ./cli.js --bool
Usage: cli <string> [options]

Arguments:
  <string>  A required string argument                  [required] [string]

Options:
  -b, --bool          A boolean option                            [boolean]
  -n, --num <number>  A number option                              [number]
  -h, --help          Show help                  [commands: help] [boolean]
  -v, --version       Show version number     [commands: version] [boolean]

Missing required argument: string

Validation failed and sywac printed the help text with an error message. Let's check the exit code of that last run:

$ echo $?
1

This is a good sign that our CLI will play well with others.

API

For details on the full API, go to http://sywac.io

License

MIT

sywac's People

Contributors

dependabot-preview[bot] avatar elliot-nelson avatar greenkeeper[bot] avatar nexdrew avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

sywac's Issues

Add "--no-flag" support for Boolean types

SUMMARY

In many popular CLI frameworks, having a boolean flag --example also allows --no-example, as an alias for --example false. Sywac should support the same input for boolean flags.

DETAILS

We'll need to update the boolean type to slurp up this alternate alias, and make sure that it is handled properly when strict mode is turned on.

Prompt user for input

Is there any specific sywac API for prompting -> capturing user input from the terminal, or is the intention for the developer to do this bespoke?

Thanks.

Can we turn on a default colorful help text?

Does Sywac have a way to tell it to output the default help text with a colorful default scheme?

I'm thinking of something like Caporal's default help text which has nice default coloring:

I am aware Sywac has a .style feature, but that requires customization (which is good for those users!), but it'd be nice to have default colored help text.

Lack of documents

Lots of them are under construction.
BTW sywac itself is great and easy to use with no extra dependency. Good work

An in-range update of coveralls is breaking the build 🚨

The devDependency coveralls was updated from 3.0.3 to 3.0.4.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

coveralls is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ❌ continuous-integration/travis-ci/push: The Travis CI build failed (Details).

Commits

The new version differs by 5 commits.

  • 8ac4325 version bump
  • 9d9c227 Bump extend from 3.0.1 to 3.0.2 (#226)
  • 33119a7 Bump js-yaml from 3.11.0 to 3.13.1 (#225)
  • f5549c7 Bump handlebars from 4.1.0 to 4.1.2 (#224)
  • 4df732b Style fix (#211)

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Better error generation if file doesn't exist

It would be good if when type was 'file' that if the file didn't exist at the path specified by defaultValue that parsed.j would be falsy so we could handle the error.

I just found this bug where if the file at path doesn't exist, nothing happens. We had to implement some hacky logic to make error handling occur. See this change.

trying to set a default on required parm

I am trying to set a default on a required parm and have the following coded
image

When i display the command I see this:
image

no default is assigned nor is the default (*) displayed .
I must be doing something wrong but nothing jumps out at me.

Best practice for error output?

I find myself repeating a pattern with most of my CLI programs, curious if there is an alternate take.

Usually there are 3 classes of CLI errors:

  1. Argument/command line validation error - in this case, I probably want to detect it early, in check(), and generate usage details + error via cliMessage.

  2. Expected error cases - in this case, I don't want usage help, but would like to print an error message with no stack trace.

  3. Unexpected error cases - an exception (from my code or a library) bubbled up, in this case I want a full stack trace for debugging.

Here's a complete example:

class SimpleError extends Error { }

sywac
    .command({ /* details */ })
    .command({ /* details */ })
    .parse().then(result => {
        if (result.errors && results.errors[0] instanceof SimpleError) {
            console.error(chalk.red(result.errors[0].message));
        } else if (result.errors && result.errors[0]) {
            console.error(chalk.red(result.errors[0].stack));
        } else if (result.output) {
            console[result.code === 0 ? 'log' : 'error'](result.output);
        }
    })

(The parseAndExit nicely handles the last part for you, but you can't insert your custom error class handling above if you use that.)

I'm curious if this tracks for you, creating a token "error" class in every CLI program so you can distinguish between expected and unexpected errors. I've created this token error class in so many different utilities and libraries that I sometimes wish the concept of a UserError - a thrown Error containing a message intended for the user - existed in core nodejs, and then sywac could handle it for me by printing the .message if it's a UserError or the .stack if it's not.

Weird help message layout when desc string wraps

Hello

Not sure if this is intentional but when a single desc string in the help message is too long, the hint string drops to a new line for every single option within that group and there's an extra new line after each option.

Here's the normal version where no lines wrap:

screen shot 2018-03-20 at 17 18 24

And here's where a single line wraps:

screen shot 2018-03-20 at 17 20 10

You'll notice how the help and version lines wrap as well.

I've tried playing around with the style function but haven't found a way around this. Ideally I'd like the help and version lines to look the same as in the first picture and not a have new line below the line that wraps.

Thanks for a great library!

Should certain stdout messages go to stderr?

I noticed this issue while experimenting with strict mode. When writing small unix-style utilities, it's nice if only expected output ends up on stdout, and other output ends up on stderr.

An example:

// find-recent-project-files
#!/usr/bin/env node
require('sywac')
    .strict()
    .option('--client <string>', 'specify which client the project was for')
    .then(argv => {
        /* list the project files */
    });
# touch recent project files for acme client
$ find-recent-project-files --clent acme | xargs -t touch

I think what I'd want in this situation is for find-recent-project-files to immediately write its usage information and error message to stderr, where I can see it, and then write nothing ("") to stdout, which will be read and ignored by xargs.

Instead what happens is that I don't see the error message, except in little bits as spam from xargs attempting to "touch" a bunch of random words and punctuation.

(This is probably a breaking change, but overall might make sywac's behavior more predictable.)

Option flags based on position in argv

I'm trying to port an old CLI interface to sywac and running into an edge case that's tricky.

This configuration tool has options that do different things depending on whether they are before or after commands. For example:

slalom query -l 'retired' -i '10.11.*' update -l 'Retired'

There's a bunch of options (imagine -l, --label, -n, --name, etc.), but they mean "look for" in the first half of the CLI and "update" in the second half. I've been playing with nested command blocks and setting up options, but it looks like there is no way to get at the value of the first -l (it is overwritten by the second globally).

Is this just a Sywac thing, where all "options" for the command line are considered global? If so, I'll probably have to drop down to manually parsing args directly.

Best way to add implicitcommands?

Similar to version or help, Is it possible to add an option that triggers a command on the root?

For example:

cli.js --root-option                    [command: root-option] [boolean]

would run a root-option command just like how --help and -v run their respective commands.

I've tried to do this by using aliases, but having an alias with a '-' prefix doesn't get recognized.

Any suggestions?

Default commands with positional args behave unexpectedly

An example program:

require('sywac')
    .command('kiss <player>', argv => console.log(`You kissed ${argv.player}`))
    .command('attack <player>', argv => console.log(`You attacked ${argv.player}`))
    .command('* <player>', argv => console.log(`You tried to ${argv._[0]} ${argv.player}`))
    .parseAndExit()
$ ./example kiss Susan
You kissed Susan

$ ./example attack Brenda
You attacked Brenda

$ ./example examine Dave
You tried to Dave examine

The issue, I think, is that when we begin parsing for the implicitly matched default command, _ contains ['examine', 'Dave']. We then process all positional args from the DSL, mistakenly applying the first (examine) to player, leaving Dave sitting in _.

Filing as a bug because I think it's clearly wrong for argv.player to be examine in this situation, but I'm not sure on the right solution. We could slurp examine into argv['*'] and the problem would go away, but that might cause other issues (it would totally prevent two nested implicit commands, for one thing).

Respect ANSI codes when applying maxWidth

This was discussed originally in #46 but I thought I'd break it into a separate (possibly solvable) issue.

That discussion reminded me of https://github.com/chalk/wrap-ansi, which might solve the problem outright if we are willing to add a (small but certainly not zero weight) dependency to sywac.

It's not highest priority but I think scratching this issue off the list makes it easier to push/promote the idea of user-created styles for sywac, since you wouldn't need to worry about strange splitting if you don't massage the maxWidth.

Convert kebab-case flags to camelCase

This difference between commander and yargs and sywac is actually the difference I noticed most when converting some prior CLIs - I had some programs with many (100?) flags, and ended up writing wrappers to convert parameter names instead of renaming them.

What do you think about supporting this use case? I could see three viable paths:

  1. Breaking change: start converting all kebab to camel. Breaks existing programs (potentially).

  2. Add a boolean option defaulting to false for auto converting. Suffers a little from "yet another option", but gives the same behavior for essentially free.

  3. Support more generically - some kind of "flag conversion" or "flag formatting" option. Or maybe even takes a full argv and allows you to define a key converter? This maybe suffers from "yet another option" AND "how the hell does it work".

Interested in your thoughts and how hot/cold you'd be for support for this feature.

An in-range update of coveralls is breaking the build 🚨

The devDependency coveralls was updated from 3.0.8 to 3.0.9.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

coveralls is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ❌ continuous-integration/travis-ci/push: The Travis CI build failed (Details).

Release Notes for Maintenance, dependency refinement
  • removed Istanbul, mocha dependencies
  • reverted cobertura-parse support (package dependency archived)
  • removed snyk (automated security updates from GitHub via Dependabot enabled)
  • improved Windows support
Commits

The new version differs by 15 commits.

  • 2e2b108 bump version
  • 50913ed Remove the now unused istanbul and mocha-lcov-reporter devDependencies
  • 1bceeff Revert #243.
  • 4aa11a2 Remove snyk.
  • 33eccc3 Revert "CI: use npm ci on Node.js >=8."
  • 236529b Update logger.js
  • 3a90b07 fix equality operator in logger.js
  • bbe2de5 Update package.json
  • 07ef879 Fix logger regression.
  • f58f8b9 README.md: Add GitHub Actions CI info.
  • 0488f10 Tweak README.md
  • be79dab CI: use npm ci on Node.js >=8.
  • ef51945 CI: Add Windows testing and lint
  • c202346 Fix Windows tests again.
  • 3d82534 use Coveralls GitHub action

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

An in-range update of tap is breaking the build 🚨

The devDependency tap was updated from 12.5.1 to 12.5.2.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

tap is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ❌ continuous-integration/travis-ci/push: The Travis CI build failed (Details).

Commits

The new version differs by 10 commits.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Trying to get in touch regarding a security issue

Hey there!

I'd like to report a security issue but cannot find contact instructions on your repository.

If not a hassle, might you kindly add a SECURITY.md file with an email, or another contact method? GitHub recommends this best practice to ensure security issues are responsibly disclosed, and it would serve as a simple instruction for security researchers in the future.

Thank you for your consideration, and I look forward to hearing from you!

(cc @huntr-helper)

Customize "--" flag that delimits extra arguments

CONCEPT

Allow an Api-level, synchronous config method extraDelimiter, which takes a function that accepts a string and returns a boolean (or, alternately, you can pass a raw string). This function/string would be passed as a second argument to slurpArgs and used instead of the hard-coded (linux default) --.

ADDITIONAL CONTEXT

In many linux tools, the argument -- means "what comes after this should not be processed". Sywac currently uses this same style, and will not process any additional flags or parameters after the arg -- is encountered on the command line. This feature would allow the user to modify this flag to something else, such as --args, or another custom string.

APPLICATIONS

Why would you ever want this? There's a couple powerful things you could do here:

// Disable "extra mode" altogether, if desired
sywac.extraDelimiter(() => false)

// Use a different syntax for your specialized CLI
sywac.extraDelimiter('#')

You could even provide an interface that "fakes out" a flag that gives unprocessed args:

sywac
    .boolean('--print', 'print the rest of the args to the terminal')
    .extraDelimiter('--print')

Seems like this would be pretty easy to implement and wouldn't step on toes for most people that don't need it... any thoughts?

parse vs parseAndExit

Hi @nexdrew

What's the correct "recommended" way to establish whether to parse or parseAndExit?

I have the following code (cli.js) which feels a bit dirty and is also missing a few cases... which I think
sywac should be dealing with. For example, when a user runs the CLI with test <unknownOption> parseAndExit executes my test command and I'm expecting the command to handle and spit out the specific command (test in my case) help along with a contextual help error message, which doesn't happen.

Also when a user runs the CLI with an (any) unknown command (for example testttt), just the generic help is displayed, I think sywac can and usually does also produce a contextual error message which is not currently the case. Am I doing something incorrectly here?

cli.js

const processCommands = async (options) => { // eslint-disable-line no-unused-vars
  log.debug('Configuring sywac\n', { tags: ['cli'] });
  const api = await sywac // eslint-disable-line no-unused-vars
    .usage('Usage: $0 [command] [option(s)]')
    .commandDirectory('cmds')
    // This overrides the --help and --version and adds their aliases
    .showHelpByDefault()
    .version('-v, --version', { desc: 'Show version number' })
    .help('-h, --help')
    .preface(figlet.textSync(pkg.name, 'Chunky'), chalk.bgHex('#9961ed')(pkg.description))
    .epilogue('For more informatiion, find the manual at https://docs.purpleteam-labs.com')
    .style({
      // usagePrefix: str => chalk.hex('#9961ed').bold(str),
      flags: str => chalk.bold(str),
      group: str => chalk.hex('#9961ed').bold(str),
      messages: str => chalk.keyword('orange').bold(str)
    });

  const shouldParseAndexit = (argv) => {
    const command = argv[2];
    const arg = argv[3];
    return argv.length < 3
      || command === 'about'
      || command === '-v' || command === '--version'
      || command === '-h' || command === '--help'
      || (command !== 'test' && command !== 'testplan')
      || (command === 'test' && !!arg) || (command === 'testplan' && !!arg);
  };

  const cliArgs = shouldParseAndexit(options.argv) ? await api.parseAndExit() : await api.parse();

  if (cliArgs.errors) log.error(cliArgs.errors);
};

The test command (test.js)

const config = require('config/config'); // eslint-disable-line no-unused-vars
const api = require('src/presenter/apiDecoratingAdapter');


exports.flags = 'test';
exports.description = 'Launch purpleteam to attack your specified target';
exports.setup = (sywac) => {
  sywac
    .option('-c, --config-file <config-file path>', {
      type: 'file',
      desc: 'Build user supplied configuration file. Must be a file conforming to the schema defined in the purpleteam documentation.',
      mustExist: true,
      defaultValue: config.get('buildUserConfig.fileUri')
    });
};
exports.run = async (parsedArgv, context) => {
  if (parsedArgv.c) {
    const configFileContents = await api.getBuildUserConfigFile(parsedArgv.c);

    await api.test(configFileContents);

    //  stream tester log           Print each tester to a table row, and to log file
    //  stream slave log            To artifacts dir
  } else {
    context.cliMessage('You must provide a valid build user configuration file that exists on the local file system.');
  }
};

Thanks.

Sywac -support for position parameters?

I have a need to be able to support positional parameters for example:
cli.js jobs status --jname

where jobs and status are both positional parameters and jname is a required subparm.
I know yargs can handle this but I haven't been able to find any documentation or examples on how to accomplish this in sywac. I also need to have the help auto generated for this. So a user could enter
cli.js --help
cli.js jobs --help
cli,js jobs status --help

Are there any samples of how i can accomplish this available? i have looked in the doc at sywac.io but not found anything.

Proposal for "run" handler at Api level

SUMMARY

Broke this out from #63 so it can be discussed separately. The idea is to have a "run" handler for non-command CLIs, so that when the program's logic executes happens in the same place in either case.

EXAMPLE

/* Today */
sywac
  .string('foo')
  .parse()
  .then(argv => console.log('OK'))

/* Suggested API */
sywac
  .string('foo')
  .run(argv => console.log('OK'))
  .parse()

DISCUSSION

Implementation of this feature would be very easy, but explaining it to a user might not be. The issue with supporting "run" (i.e.: what should this program do when it is parsed or parsedAndExit'd?) is that it conflicts with the command's run:

sywac
  .command({
    setup: sywac => {
      sywac
        .check(/* special validation for the args for this command */)
        .run(argv => console.log('Nested Api Run?'))
    },
    run: argv => console.log('Command Run?')
  })
  .run(argv => console.log('Api Run?')
  .parseAndExit()

If I ran myapp start, which run function in this program should I expect to execute? It's not clear and I'm not sure how I would explain that to a user.

Welcome, Hacktoberfest 2020!

Hello, Hacktoberfest!

This repo contains the Sywac CLI project. If you're interested in contributing some code, please check out the list of issues labeled Hacktoberfest. We're also looking for people who might be interested in contributing some documentation / technical writing, for that, see the list of sywac-website issues instead.

Have fun! πŸ‘‹

An in-range update of tap is breaking the build 🚨

The devDependency tap was updated from 14.8.0 to 14.8.1.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

tap is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ❌ continuous-integration/travis-ci/push: The Travis CI build failed (Details).

Commits

The new version differs by 3 commits.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

feature: document exit codes in help text

SUMMARY

I was recently working on an internal tool that needed some specific exit codes to play well with other tools, and I tried my hand at documenting the exit codes in the help screen itself. I took a screenshot:

exitcodeexample

Would it be useful if a user could add information about exit codes directly to their help screen with an API method?

Here's the code I used to add this info (I cheated and used the epilogue, and made calls into my style object for the styling):

    function formatHelpTextHash(header, hash) {
        const style = require('sywac-style-chunky');
        const keyLength = Math.max(...Object.keys(hash).map(key => String(key).length));

        return [
            style.group(header),
            ...Object.keys(hash).map(key => `  ${style.usageOptionsPlaceholder(key).padEnd(keyLength)}  ${hash[key]}`)
        ].join('\n');
    }

    sywac.epilogue(formatter.formatHelpTextHash({'Exit Codes:', {
            0: 'success',
            1: 'unexpected error',
            2: 'one or more nodes over the --consecutiveReboots limit',
            3: 'one or more nodes exactly at the --consecutiveReboots limit'
    });

DETAILS

Add an optional help text API method exitCodes to describe a list of exit codes and their meanings. Does not appear in help screen unless specified. Formatted using your styling... somehow. (I used .usageOptionsPlaceholder just because I liked the look in my case, but it could be more appropriate to create a specific style for this. Or we could pick something like .hints.)

Prep for Hacktoberfest

If we can come up with a list of relatively easy features on the roadmap that first-time contributors could tackle successfully, and aren't just busy work, it would be nice to throw them up for Hacktoberfest this year.

Ideally we can get them written up (or just apply a little polish if they are already written) in mid-late September, then we'd be ready to slap on the label at beginning of October. Free labor and exposure for sywac, useful and interesting work to do for OSS developers (and potentially new fans of sywac afterwards!).

buildInvalidMessage hangs, then crashes due to out of memory.

When buildInvalidMessage returns, execution hangs for about 20 seconds. Then:

<--- Last few GCs --->

[12069:0x2756b60]   159299 ms: Mark-sweep 4562.8 (4611.2) -> 4562.8 (4611.7) MB, 277.2 / 0.0 ms  (+ 1803.3 ms in 7421 steps since start of marking, biggest step 19.2 ms, walltime since start of marking 3997 ms) allocation failure GC in old space requested[12069:0x2756b60]   162828 ms: Mark-sweep 5341.1 (5402.7) -> 2472.0 (2496.4) MB, 510.6 / 0.0 ms  (+ 1002.9 ms in 4154 steps since start of marking, biggest step 18.6 ms, walltime since start of marking 2408 ms) allocation failure GC in old space requested

<--- JS stacktrace --->

==== JS stack trace =========================================

Security context: 0x1197d9525ee1 <JSObject>
    0: builtin exit frame: getOwnPropertyNames(this=0x1197d95046c9 <JSFunction Object (sfi = 0xd561d209d81)>,0xba0f328bb41 <JSArray[75209228]>)

    2: arrayIndexes [/* anonymous */:2] [bytecode=0x1687d8032f91 offset=248](this=0x3a5bef303a21 <JSGlobal Object>,object=0xd561d202351 <the_hole>)
    3: next(this=0x33aff79a00f9 <JSGenerator>)
    5: packRanges [/* anonymous */:4] [bytecode=0x1687d803...

FATAL ERROR: invalid table size Allocation failed - JavaScript heap out of memory
 1: node::Abort() [node]
 2: 0x121a2cc [node]
 3: v8::Utils::ReportOOMFailure(char const*, bool) [node]
 4: v8::internal::V8::FatalProcessOutOfMemory(char const*, bool) [node]
 5: v8::internal::OrderedHashTable<v8::internal::OrderedHashSet, 1>::Allocate(v8::internal::Isolate*, int, v8::internal::PretenureFlag) [node]
 6: v8::internal::OrderedHashTable<v8::internal::OrderedHashSet, 1>::Rehash(v8::internal::Handle<v8::internal::OrderedHashSet>, int) [node]
 7: v8::internal::OrderedHashTable<v8::internal::OrderedHashSet, 1>::EnsureGrowable(v8::internal::Handle<v8::internal::OrderedHashSet>) [node]
 8: v8::internal::OrderedHashSet::Add(v8::internal::Handle<v8::internal::OrderedHashSet>, v8::internal::Handle<v8::internal::Object>) [node]
 9: v8::internal::KeyAccumulator::AddKey(v8::internal::Handle<v8::internal::Object>, v8::internal::AddKeyConversion) [node]
10: 0xe155e1 [node]
11: v8::internal::KeyAccumulator::CollectOwnElementIndices(v8::internal::Handle<v8::internal::JSReceiver>, v8::internal::Handle<v8::internal::JSObject>) [node]
12: v8::internal::KeyAccumulator::CollectOwnKeys(v8::internal::Handle<v8::internal::JSReceiver>, v8::internal::Handle<v8::internal::JSObject>) [node]
13: v8::internal::KeyAccumulator::CollectKeys(v8::internal::Handle<v8::internal::JSReceiver>, v8::internal::Handle<v8::internal::JSReceiver>) [node]
14: v8::internal::KeyAccumulator::GetKeys(v8::internal::Handle<v8::internal::JSReceiver>, v8::internal::KeyCollectionMode, v8::internal::PropertyFilter, v8::internal::GetKeysConversion, bool) [node]
15: v8::internal::Builtin_ObjectGetOwnPropertyNames(int, v8::internal::Object**, v8::internal::Isolate*) [node]
16: 0x17f74448441d

Also setValue is executed for multiple types.

Throw validation error on unknown flag

Hi!

Is it possible to have the parser exit or throw an error when a given flag is invalid? Say I have something like:

// cli.js
sywac
  .boolean("--dry-run", {
    desc: "Dry run",
  })
  .command("delete-all", {
    desc: "Deletes everything",
    run: (argv) => {
      if (argv["dry-run"]) {
        console.log("no harm done")
      } else {
        // wreak havoc
      }
    },
  })
  .help("-h, --help", { implicitCommand: false })
  .parseAndExit()

Now running this (notice the misspelled flag name):

# bad times
$ node cli.js delete-all --dyr-run

will indeed wreak havoc.

Currently I'm solving this by doing something like the following in the check function:

const knownFlags = flatten(
  ctx.details.types
    .filter(t => t.datatype !== "command")
    .map(t => t.aliases),
)
const receivedFlags = Object.keys(argv)

and comparing the two arrays. But is there a more canonical way to solve this?

-Eythor.

EDIT: For completeness the entire cli would look like this:

// Uses flatten and difference helper functions from lodash
sywac
  .boolean("--dry-run", {
    desc: "Dry run",
  })
  .command("delete-all", {
    desc: "Deletes everything",
    run: (argv) => {
      if (argv["dry-run"]) {
        console.log("no harm done")
      } else {
        console.log("wreak havoc")
      }
    },
  })
  .help("-h, --help", { implicitCommand: false })
  .check((argv, ctx) => {
    const knownFlags = flatten(
      ctx.details.types
        .filter(t => t.datatype !== "command")
        .map(t => t.aliases),
    )
    const receivedFlags = Object.keys(argv)
    if (difference(receivedFlags, knownFlags).length > 0) {
      ctx.cliMessage("Invalid flag")
    }
  })
  .parseAndExit()

Argument with name 'version' is not working

If we use '--version' as an argument instead of the program version, the value is null. My app would look like:
$ my-command list-configuration --version=2
My command is always getting the version argument as null

How to provide commands with additional data?

I'm creating a chatbot and, with yargs, I was able to .parse a command and pass to it an arbitrary object which would be available, as part of the argv object, for further processing to all sub-commands in other directories.

Is there an equivalent for sywac?

sywac and Typescript

Using Typescript.
Getting a rogue line displayed when showing a help screen after using typescript nd running the resultant JS.
for example:
node cli -h
gives this:

Usage: cli [options]

Commands:
com Issue commands
files File related functions
info System information
jobs Job status

str => chalk_1.default.cyan.bold(str)
(str,

Global Options:
-h, --help Show help

The "str =>" line appears in the top level help screen only (and is displayed in bold cyan). The problem doesn't appear to occur in any of the lower level command helps.

I originally had my own styles module mirrored on the doc sample but commented that out but the problem still persists.
the only item I found close to this particular text is in the styles module line and states this:
group: str => chalk.cyan.bold(str),

Easy to recreate if required.

image

Is pkg actively maintained

I just found this package want to use it, but I wonder if its actively maintained as the last update is 1 year ago. And docs still under construction.

Control style with a flag

I was looking into how to control output styling with a flag.

It seems the best bet is probably to do two different passes of sywac - the first only checks for the flag (for example, a "compact" flag, that uses a condensed style instead of a nice colorful style), and the second does the complete, complicated API of your app.

However, since the parsing happens asynchronously, you actually need to set up the second one inside an async function... something like this:

const sywac = require('sywac');
const Api = require('sywac/api');
const fancyStyle = require('sywac-style-fancy'); // made up
const compactStyle = require('sywac-style-compact'); // made up

async function main() {
  const firstPass = await (new Api().boolean('compact').parse());
    
  sywac
    .strict()
    .boolean('compact', { hidden: true })
    .style(firstPass.argv.compact ? compactStyle : fancyStyle)
    .command(...)
    // ... another 100 lines of fancy options and commands here
    .parseAndExit();
}

main();

Is there a simpler method than this for using an incoming flag to control an aspect of the synchronous configuration of your API? (A similar example might be a program with a hundred flags, one of which is actually --strict itself - turning on strict mode to warn if some other flag is misspelled.)

How to prevent unknown options

Ideally, I'd like any command line option that is not listed on the usage screen to generate an error. I don't see any kind of preventUnknownOptions() type API after browsing.

Is there a way I could do this today? I imagine there might be some way to look through the options and find the ones of UnknownType, but I don't know how I could do that in a way that would apply globally to all my commands and subcommands.

(As an aside, and a nice-to-have: if this doesn't exist and it's added in the future, it might also be nice to have configurable control over the behavior if the error is triggered, so that I could hook into it and do a git-style "Unknown option --nmae, did you mean --name?" error message.)

Required positionals unless flag specified?

The interface I'd like for this command is that it requires all its arguments, unless you specify the optional --interactive flag, in which case you should specify none of them.

foo <a> <b> <c>
foo -i/--interactive

I'm guessing the best way here is to just set up 3 positional arguments and the flag all as optional and do my own validation inside my own method, but then override the entire usage text? Opening issue in case there is a better way I'm missing.

cliMessage usage instructions for command print with wrong context?

Here is an example program:

require('sywac')
  .command({
    aliases: 'hello <name>',
    desc: 'say hello',
    run: (argv, context) => {
      context.cliMessage('fatal error');
    }
  })
  .help()
  .parseAndExit();

If I run --help on either the main program or the command, it prints what I would expect:

❯ ./example.js --help
Usage: example <command> <args> [options]

Commands:
  hello <name>  say hello                                                           [aliases: hello]

Options:
  --help  Show help                                                       [commands: help] [boolean]

❯ ./example.js hello --help
Usage: example hello <name> [options]

Arguments:
  <name>                                                                         [required] [string]

Options:
  --help  Show help                                                       [commands: help] [boolean]

Now, if I generate a built-in error (by forgetting to include the name), the error prints as I expect - I get the second usage instructions above. But if I generate one manually using context.cliMessage, like in my example, I get a hybrid, where the arguments are from my command but everything else is from the root program:

❯ ./example.js hello world
Usage: example <command> <args> [options]

Commands:
  hello <name>  say hello                                                           [aliases: hello]

Arguments:
  <name>                                                                         [required] [string]

Options:
  --help  Show help                                                       [commands: help] [boolean]

Oops!

I would expect my (sub)command's context to print usage instructions related to my (sub)command. Am I using cliMessage incorrectly, or is this a bug?

Custom validation without using custom types

Hi @nexdrew

My apologies if I'm missing something obvious but I haven't been able to find a way to do custom validation on the basic types (string, boolean, number, etc). I understand the library performs this validation for me if I set strict: true and that I can also add my own custom types with custom validation.

But would I be able to do something like the following per option / positional argument.

sywac.option("-f, --foo", {
  type: "string",
  validate: () => {/* my custom validation logic */},
})

Thanks!

process exit

How to stop the process exiting? Need to keep it running to accept SSE's to run an event listener.

How to specify certain options are required?

I'd like, for example, for two or more specific options to be required, and for that to appear as a detail in the help output in case any of the required options are not present. Is there an API to tell options to be required?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.