mattallty / caporal.js Goto Github PK
View Code? Open in Web Editor NEWA full-featured framework for building command line applications (cli) with node.js
License: MIT License
A full-featured framework for building command line applications (cli) with node.js
License: MIT License
Failing program:
import * as program from 'caporal'
program.version('0.1.0').name('yarn run make')
Property version does not exist on type 'typeof 'caporal''
Type-casting to any
and it works.
Successful program (when it shouldn't be):
import program from 'caporal'
program.version('0.1.0').name('yarn run make')
The compiled code fails with TypeError: Cannot read property 'version' of undefined
I'm trying to get data from user by console to solve quadratic. It run successfully even when I entered 3 float numbers.
But when I enter Negative number it does not work, eg:
node caporalDemo.js solve 7 8 -2
it say
Error: Wrong number of argument(s) for command solve. Got 2, expected exactly 3.
My caporalDemo.js --help
:
caporalDemo.js 1.0.0
USAGE
caporalDemo.js solve <a> <b> <c>
ARGUMENTS
<a> Value of a required
<b> Value of b required
<c> Value of c required `
So How can I input Negative arguments ?
Is it possible to define flag-like options that take zero parameters, e.g. --verbose
? It seems that value following the option gets always assigned as option value and I haven't found a way around this behaviour.
my_program --verbose test
should workmy_program test --verbose
should not workIn the first case --verbose
is an option for my_program
, in the second case it's an option for the test
command.
Hi I think it will be nice if you support typescript type definition.
When using the --no-color
flag, I get colorless output, but I also get this error:
Error: Unknown option --color.
Type stacker --help for help.
Hi,
After updating to Caporal 0.5.0, the commands in my help are missing.
If I remove the default command, by removing .action from the below snippet, all the commands appear again in help:
prog
// default command
.version(VERSION)
.action(commands.nextUndoneAction)
.description(COMMAND_DESCRIPTIONS.default)
I'm talking about Moro, and here's the link to that part of code: link to repo
What I expect?
All the commands appear in help
What I get?
No help for specific commands is listed in help.
Something like this doesn't get properly colorized:
prog.option ( '--config <path|object>', '' );
Because |
is not included in the allowed characters matched by the regex, for no apparent reason.
Running this example on node v7.6.0 gives no output at all
https://github.com/mattallty/Caporal.js#simple-program-single-command
When using the --quiet
flag like this: my_program --quiet test
I get usage info for the test
command.
Great package ๐
How we can change the app name ? By default, it seem to be the filename.
It will be nice if we can do :
prog('PizzaHut')
.version('1.0.0')
or
prog
.pkg('PizzaHut')
.version('1.0.0')
Edit :
Solved. Caporal export an object (Program) with some Getter and Setter extended.
We can setup "name", "description" and "version" like that : ๐ฅ
prog
.name('PizzaHut')
.bin('pizza.js')
.description('The PizzaHut CLI')
.version('1.0.0')
The built-in logger does not work in asynchronous functions, but console.log
does.
The following very simple app demonstrates the problem clearly:
#!/usr/bin/env node
const cli = require('caporal');
cli.version('0.0.1');
cli.command('console', 'A Test Command using console').action(testConsole);
cli.command('logger', 'A Test Command using logger').action(testLogger);
cli.parse(process.argv);
async function testConsole(args, opts, logger){
console.log('boo!');
let p = await Promise.resolve(true);
console.log('hiss!');
return p;
}
async function testLogger(args, opts, logger){
logger.log('boo!');
let p = await Promise.resolve(true);
logger.log('hiss!');
return p;
}
Here's what happens when I run it with the latest Caporal on the latest nodeJS:
bart-iMac2013:vtemp bart$ node --version
v8.2.1
bart-iMac2013:vtemp bart$ ./index.js console
boo!
hiss!
bart-iMac2013:vtemp bart$ ./index.js logger
bart-iMac2013:vtemp bart$
Would you consider adding dot notation support? (See yargs example).
It would works as follow:
myprog --user.name Batman
// options = {user: {name: 'Batman'}}
Currently it's possible to defined the option user.name
and then parse it later. But that forces us to define of the possible "suboption" otherwise we get Error: Unknown option --user.name.
In many cases this time of notation is used to generate and options passed to a third-party module for which the possible options are not necessarily know or can numerous. Therefore it's not always possible or a good idea to list all the potential suboption and print them in the help.
Maybe we could allow to do something like this:
prog
.option('--user <user>', 'User options', prog.DOT)
The help would look like this:
OPTIONS
--user <user> User options
Optionally the user could use a custom transform in place of prog.DOT
that would validate which suboption are allowed.
If all that is too complex or out of scope maybe we could just add an option to not return the Error: Unknown option --user.name.
when the option user
has been defined. Basically if user
option is defined with some sort of "accept dot notation" flag, then accept any user.*
options. And it would be up to the user to transform/validate in the action
.
I'm using the logdown module as my custom logger, which is one of winston-compatible loggers. This module provides a functionality to attach a tiny emoji and a custom prefix to every messages like below.
const logger = require('logdown')('caporal:');
logger.info('this is a test message');
โน caporal: this is a test message
The problem is the custom logger attaches the meta data(i.e., emoji and prefix) into the help instructions as well. This is because the help.js utilizes program.logger object to print out the help instructions. So, when I set my custom logger, the help instructions are like below.
โน caporal:
caporal test 1.0.0
USAGE
The logdown logger may be not the only one that has this problem. I think the help instruction message should be independent with custom logger because it's auto-generated by Caporal.js. How about just using the default console.log function to print out the help instructions?
Please let me know there is any point that I misunderstood or missed ๐
Hey, is it possible to add a default command? I did not find this mentioned on the documentation.
I am using corporal for the npm module "z1". It is a module that allows you to easily cluster node-apps.
In my code I am using caporal like this:
This is my completion function:
In this example case it returns a promise that resolves to the following array:
['exampleApp:/home/jonathan/Desktop/z1/example']
Now I am trying to use the completion:
<tab> <tab>
The completion for the [appName] argument is not showing up.
I am selecting one of the two options.
<tab> <tab>
The completion for the [appName] is showing up (last in the list) where it should not.
I am not sure, if I am doing something wrong and it would be nice if you could help me out.
(Here is the complete CLI file https://github.com/robojones/z1/blob/caporal/cli/index.js)
The help command/opt seems to only be outputting high-level program usage information, i.e list of commands and required input.
Caporal automatically generates help/usage instructions for you. Help can be displayed using -h or --help options, or with the default help command.
Makes sense for the framework to also support usage information on the command level.
pizza order -h
In this example, the help usage should show any required arguments for this command as well as the options and their descriptions.
Recording of Issue
It would be great to see documentation on why this was developed and how it differs from existing libraries such as yargs.
We have this project that needs to work across platforms and architecture, so we make sure to test them all out. For some reason Caporal fairly consistently fails on the postinstall step, but only on X64 windows and windows 8. Here's the error output, coming from appveyor:
> [email protected] postinstall C:\projects\pact-node\node_modules\caporal
> (test -f ./node_modules/husky/bin/install.js && node ./node_modules/husky/bin/install.js) || exit 0
npm ERR! path C:\projects\pact-node\node_modules\fsevents\node_modules
npm ERR! code EPERM
npm ERR! errno -4048
npm ERR! syscall scandir
npm ERR! Error: EPERM: operation not permitted, scandir 'C:\projects\pact-node\node_modules\fsevents\node_modules'
npm ERR! { Error: EPERM: operation not permitted, scandir 'C:\projects\pact-node\node_modules\fsevents\node_modules'
npm ERR! stack: 'Error: EPERM: operation not permitted, scandir \'C:\\projects\\pact-node\\node_modules\\fsevents\\node_modules\'',
npm ERR! errno: -4048,
npm ERR! code: 'EPERM',
npm ERR! syscall: 'scandir',
npm ERR! path: 'C:\\projects\\pact-node\\node_modules\\fsevents\\node_modules' }
npm ERR!
npm ERR! Please try running this command again as root/Administrator.
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\appveyor\AppData\Roaming\npm-cache\_logs\2017-11-10T00_07_01_211Z-debug.log
Command exited with code -4048
It would be great if this could be fixed as it's failing our CI builds. Thanks.
Maybe I'm missing something in the docs, but seems like [] and <> have slightly different semantics when specified in named options vs. in positional arguments. For options, the angle brackets appear to mean an option value is required whenever its associated flag is passed, whereas in positional arguments they indicate the given argument is always required. What I'd like to be able to do is always require that an option to be passed, in the example below for example i'd like a way to always require "stage" for example, yet it is treated
-s, --stage <stage> Target environment [dev, staging, prod] optional
-a, --apps <apps> Apps to deploy (comma separated list) optional
-s, --stage [stage] Target environment [dev, staging, prod] optional
-a, --apps <apps> Apps to deploy (comma separated list) optional
perhaps something like the following?
-s --stage {stage} Target environment [dev, staging, prod] required
Hello,
Thanks for the nice project!
I'm using Caporal for an open source project. Currently, I got a problem: when the app is run by --help
the help screen won't stop, until I press ctrl x
Any ideas?
I notice that if I write a custom function validator, any errors thrown by my validator are not logged. Instead I just get a generic error like:
Error: Invalid value 'bar' for option --foo
This is a bummer, because I would like to notify the user as to why their argument/option is invalid.
For example, I wrote a DateValidator
, to allow the users to pass in dates to my command:
function DateValidator(opt) {
const date = new Date(opt);
if (date.toString() === 'Invalid Date') {
throw new Error(`"${opt}" is not a valid date. Option value must be an ISO date string or a timestamp in milliseconds`);
}
return date;
}
I would really like to be able to show that validation error to the user, so they don't have to guess as to why their command failed. For example:
Error: Invalid value 'today' for option --start-time
"today" is not a valid date. Option value must be an ISO date string or a timestamp in milliseconds
To work around this, I overwrote the fatalError
method of Caporal:
const prog = require('caporal');
const fatalErrorOrig = prog.fatalError.bind(cli);
prog.fatalError = (err) => {
while (err.meta && err.meta.originalError) {
err = err.meta.originalError;
}
fatalErrorOrig(err);
};
This, of course, is not ideal.
I'd be willing to put together a pull request for this, if you're open to it.
According to the docs it should possible to specify an option as repeatable to provide more than one value:
Command:
./script.js concat -f file1.txt -f file2.txt
Code:
program
.version('1.0.0')
.command('concat') // concat files
.option('-f <file>', 'File to concat', program.REPEATABLE)
.action(function(args: any, options: any) {
console.log(JSON.stringify({args, options}));
});
Expected result
{"args":{},"options":{"file": ["file1.txt", "file2.txt"]}}
Actual result
{"args":{},"options":{}}
Expose a method to show the help string whenever I want to, like it being the default command of my program.
Program would be something like:
program
.version(package.version)
.action(() => program.showHelp())
.command('real command')
.action((args, options, logger) => /* do stuff */);
program.parse(process.argv);
// ./myprog
/* ... prints help ... */
I managed to print the help text using the private _help
function, but as it's private and not documented, I believe it's not good to stick to it.
Do you mind updating the package on NPM? I'd like to use those multiple help sections I've submitted a PR for, but the version served by NPM is not up-to-date.
I have this right now:
.option("--json", "Output indexes in JSON format", prog.BOOL, false)
I'd like command --json
to be interpreted as command --json=true
but it does not be the same. Is there a way to enable this?
I've discovered an issue whereby if you do not specify a long name for an option it results in no option being available as part of the options object when the action is called.
Consider the following
$ deploy -e staging
// not working
prog.version('1.0.0')
.command('deploy') // concat files
.option('-e <environment>', 'deployment environment')
.action(function(args, options) {
console.log(args, options); // => {}, {}
});
prog.parse(process.argv);
// working
prog.version('1.0.0')
.command('concat') // concat files
.option('-e, --env <environment>', 'deployment environment')
.action(function(args, options) {
console.log(args, options); // => {}, { env : 'staging' }
});
You will notice that updating the option to the format of '-e, --env ' makes the option available.
It took me a while to track down the issue, but I believe the offending line which is causing this behaviour is in ./lib/command.js
- #L315 , where camel case checks are performed. Should there be no long name defined then the option is not available.
Something along the lines of
_camelCaseOptions(options) {
return this._options.reduce((acc, opt) => {
const keyName = opt.name();
if (typeof options[opt.getLongCleanName()] !== 'undefined') {
acc[keyName] = options[opt.getLongCleanName()];
} else {
acc[keyName] = options[keyName];
}
return acc;
}, {});
}
It wasn't immediately obvious this was the issue. I wonder if this could be addressed by updating the docs or amending the code to accommodate the scenario whereby a short name is only used.
You say you took inspiration from commander js, from what I see you don't support sub-commands, which personally think is one of the most important features of a CLI framework. do you have plans for including a sub-command structure?
eg aws ec2 describe-instances
commander does this with a child process
and yargs supports this as well with require
calls
program.command(require('./command.js'))
Hi,
I'm trying to figure out a way to change the autogenerated help. Is it possible? I'd like to add some more lines to the end of it, including some examples.
Thanks for making this.
Caporal is working pretty well in my time tracker tool: Moro
...
program
.command('test', 'Some description')
.option('--f', 'File')
...
I have a command like the one above, and if I use the --f
option without a value, it will be true
.
(my_program test --f
). There should be a string validation too.
I made a temporary fix:
program.STRING = value => (typeof value === 'string' ? value : null);
...
program
.command('test', 'Some description')
.option('--f', 'File', program.STRING)
...
What i want:
caporal
.option('--foo', 'Show foo in all commands')
.command('com1', '')
.action((args, options) => {
console.log(options.foo === true)
})
.command('com2', '')
.action((args, options) => {
console.log(options.foo === true)
})
When I run:
program com1 --foo
# true
or
program --foo com2
# true
With the code
orig
.name('mycli')
.description('My cli description')
the printed help will be:
mycli undefined - My cli description
USAGE
....
If the version is not set, then it should be omitted in the help:
mycli - My cli description
USAGE
....
package.json
allow to define a command name to run the cli, which might be different than the filename in which its implemented.
There is an undocumented name(string)
function that allow to set the program name. The name set with this function is used to display the fist line of the help message but in the USAGE part.
For example with the following code:
// mycli.js
prog.name('my-cly')
.version('1.0.0')
.description('My cli description')
The help will be:
my-cli 1.0.0 - My cli description
USAGE
mycli.js
But it should be:
my-cli 1.0.0 -My cli description
USAGE
my-cli
Hello,
It seems LIST are parse as comma separated string and not regular JSON arrays.
Sample code with caporal 0.8.0
#!/usr/bin/env node
const prog = require('caporal');
prog
.version('1.0.0')
.command('order pizza')
.option('--number <num>', 'Number of pizza', prog.INT, 1)
.option('--kind <kind>', 'Kind of pizza', /^margherita|hawaiian$/)
.option('--discount <amount>', 'Discount offer', prog.FLOAT)
.option('--add-ingredients <ingredients>', prog.LIST)
.action(function(args, options) {
console.log(options);
console.log(options.addIngredients.length);
// options.kind = 'margherita'
// options.number = 1
// options.addIngredients = ['pepperoni', 'onion']
// options.discount = 1.25
});
prog.parse(process.argv);
// ./myprog order pizza --kind margherita --discount=1.25 --add-ingredients=pepperoni,onion
Sample call
./myprog order pizza --kind margherita --discount=1.25 --add-ingredients=pepperoni,onion
{ number: 1,
kind: 'margherita',
discount: 1.25,
addIngredients: 'pepperoni,onion' } // addIngredients should be ['pepperoni', 'onion']
15 // Length of options.addIngredients should be two not the total number of characters
There is different behaviour between:
./program language -e name
and ./program language name -e
when program is defined as:
program
.command('language', 'create a language')
.argument('[name]', 'name of the language', validateLanguageName)
.option('-e --errorOnChange', 'error if the language file has changed')
.action(createLanguage)
In the first case, name
is never set and is sent to createLanguage
as undefined. The validation function is never called either.
In the second case, name
is set as expected.
Is this a feature or a bug? It seems to work against other CLIs which can take flags in any order.
Hi, I've noticed that this package hasn't had a new version pushed since you added the typescript definition file.
Would it be possible to push a new version?
@mattallty I was intending to use prog.LIST and was reviewing the pizza example and found that I was not getting the expected result.
Consider the following
-add-ingredients=a,b => { addIngredients : undefined }
-add-ingredients a,b => { addIngredients : [undefined, undefined }
I reviewed the code and the issue appears to be caused by _validateWithFlags and an omission of an early return statement when running in unary mode.
As the function calls itself for each value in the Array it will not pass the subsequent validation checks.
_validateWithFlags(value, unary) {
if (unary) { return value; }
....
}
Though value.map and calling itself seems be redundant.
I was experimenting with your framework today (I like the built in logger stuff).
The README says I can deactivate the colors with --no-colors
. The global options added to the help message by the framework say --no-color
. Neither work and both result in an error :-(
Browsing the source and experimenting, revealed the magic option is --color=false
.
Just thought I should point out that the README and the generated help are incorrect.
I like your framework. Nice work!
I'm trying to pass as variable something like --params="var1=val1, var2=val2"
and receiving just var1
as result.
Is there some workaround or proper config?
This example suggests the error message (throw new Error("You can only order margherita or hawaiian pizza!");
) is returned when an error occurs. But I always get this message Error: Invalid value 'some option' for option --file.
when I throw an error.
Is this a bug or the intended behavior?
Thanks for your feedback!
When the user supplies invalid CLI arguments, the program prints its help text, but it also exits with status code 0 (which means success).
It should use a non-zero exit code, unless the user specifically requested help.
I just logged some messages including strings and objects with following code.
.action(function(args, options, logger) {
logger.warn('begin');
logger.warn({ version: 'v1' });
logger.warn({ version: 'v2' });
logger.warn('end');
});
The logger does not print out each message in a proper way (i.e., each message in a line). The output is like below.
begin
version: v1
version: v2end
I suspect that the new line character should be attached at the end of prettified message in this code. I think it should be like this.
msg += prettyjson.render(meta) + "\n";
Something like this:
prog.option ( '--source, --src, -s <path>', '' );
Is supposed to be working, but only 2 flags are parsed at most.
All of them should be considered instead.
It's useful to have the ability to hide some commands from the help
text, maybe they are supported but just for development and shouldn't be visible by default, but could be visible if executing my_app help --dev
or something like that.
example:
router.get('/${folder}', controller.${folder}.${folder}Find)
router.get('/${folder}/id/:id', controller.${folder}.${folder}FindById)
router.post('/${folder}', controller.${folder}.${folder}Create)
router.put('/${folder}/:id', controller.${folder}.${folder}Update)
router.delete('/${folder}/:id', controller.${folder}.${folder}Delete)
Feature request:
It is really helpful to have possibility for creating sub-commands like in many popular tools:
$ myprogram category command --options
This is especially helpful when your program contains generated list of commands
This would be really nice to be able to embed set of commands inside of other one.
I can't see here how you manage async actions for commands. You have promises in the complete()
method, can I return a promise in an action and have it handled correctly?
There is a reference to a hardcoded process argument in lib/program.js https://github.com/mattallty/Caporal.js/blob/master/lib/program.js#L24 This breaks our Electron application which does not contain a second argument when built. This property is not being used anywhere else in the code. In general, a library should not reference a hard coded value like that and should have sanity checks. Could you please fix that?
I've created a PR #45 but it does not pass the tests.
Thanks
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.