Commander throws error for command with description - node.js

Here is a simple example of adding command in nodejs using commander:
'use strict';
const {Command} = require('commander');
const run = () => {
const program = new Command();
console.log('CMD');
program.command('cmd [opts...]')
.action((opts) => {
console.log('OPTS');
});
program.parse(process.argv);
};
run();
In this case everything works fine, but when I'm adding description and options, commander throws an error:
program.command('cmd [opts...]', 'DESCRIPTION', {isDefault: true})
node test-commander.js cmd opts
test-commander-cmd(1) does not exist, try --help
My env:
node v8.9.3
npm 5.3.0
commander 2.12.2

That is the declared behavior of commander. From the npm page under Git-style sub-commands...
When .command() is invoked with a description argument, no .action(callback) should be called to handle sub-commands, otherwise there will be an error. This tells commander that you're going to use separate executables for sub-commands, much like git(1) and other popular tools.
The commander will try to search the executables in the directory of the entry script (like ./examples/pm) with the name program-command, like pm-install, pm-search.
So, when you add a description like you have, it'll assume you have another executable file called test-commander-cmd for the sub command.
If commander's behavior is not what you were expecting, I might recommend looking into a package I published, called wily-cli... only if you're not committed to commander, of course ;)
Assuming your code rests in file.js, your example with wily-cli would look like this...
const cli = require('wily-cli');
const run = () => {
cli
.command('cmd [opts...]', 'DESCRIPTION', (options, parameters) => { console.log(parameters.opts); })
.defaultCommand('cmd');
};
run();
// "node file.js option1 option2" will output "[ 'option1', 'option2' ]"

Related

Jest cannot test commander help function

With jest I'm not able to test commander module functions that result in process exit.
For example, if I pass the --help option or an invalid parameter like -x (see below) process.exit or process.stdout.write are not called as they should looking at the commander sources.
import {Command} from "commander";
let mockExit: jest.SpyInstance;
let mockStdout: jest.SpyInstance;
beforeAll(() => {
mockExit = jest.spyOn(process, "exit").mockImplementation();
mockStdout = jest.spyOn(process.stdout, "write").mockImplementation();
});
afterAll(() => {
mockExit.mockRestore();
mockStdout.mockRestore();
});
test("Ask for help", () => {
// Setup
const save = JSON.parse(JSON.stringify(process.argv));
process.argv = ["--help"]; // Same setting it to "-x"
const program = new Command();
program
.option("-v, --verbose [level]", "verbose level")
.parse(process.argv);
expect(mockExit).toBeCalled();
// expect(mockStdout).toBeCalled();
// Cleanup
process.argv = save;
});
What is strange is that, from the behavior of other tests, process.argv is not restored after this one.
Tests are in typescript and passed through ts-jest.
Any ideas?
Thanks!
I suggest you use .exitOverride(), which is the approach Commander uses in its own tests. This means early "termination" is via a throw rather than exit.
https://github.com/tj/commander.js#override-exit-handling
The first problem though (from comments) is the arguments. Commander expects the parse arguments follow the conventions of node with argv[0] is the application and argv[1] is the script being run, with user parameters after that.
So instead of:
argsToParse = ["--help"];
something like:
argsToParse = ['node", "dummy.js", "--help"];
(No need to modify process.argv as such.)

Nodejs - Create modules from string with the npm package 'module'

I want to load a custom module using the npm package 'module', to be able to require it like any other local folder that exports something.
I cannot find any documentation on the use of the package https://www.npmjs.com/package/module
I tried to use it without any documentation on it, but i simply cannot as i dont understand how you use it.
let myFunction = module.wrap(`module.exports = () => console.log("hej")`);```
Honestly, I've never read documentation about this but I've been using Node.js for years so I'll explain how I import modules. Given a project that has the files app.js and mod1.js and the subfolder utils with the file mod2.js, you could do something like this:
prj/mod1.js
module.exports = {
test: "testing"
}
prj/utils/mod2.js
module.exports = () => {
console.log("testing");
}
prj/app.js
var mod1 = require("mod1"),
mod2 = require("utils/mod2");
console.log(mod1.test);
mod2();
I think you are trying to create a module of your own to be able to use it at multiple places within your project ( correct me if I am wrong).
SO you can do that like :
In utils/custom.js
module.exports = {
logThis: "Hi"
}
OR
var a={
logThis:function (){
console.log('HI')
}
module.exports=a;
And then in the file where you want to access this :
In app.js
let custom=require('./utils/custom.js')

How to pass command line arguments to NodeJS launched from an executable script

How to set what would otherwise be command-line arguments to node for a NodeJS process run from a launcher script? (The sh/CMD scripts npm places into node_modules/.bin.)
Plenty of NodeJS libraries / frameworks come with their own runner script, e.g. zeit/micro or moleculer that's usually executed from a npm script. This presents a problem in development, since in my case I want to do the equivalent of:
node --inspect -r ts-node/register -r dotenv-safe/config src/index.ts
(Except, of course, that does nothing since index.ts just exports something for the runner to pick up.)
Is there some "clean", preferably generic (i.e. not specific to a given framework's runner exposing those command line params) way that I'm missing to do this, ideally one that works as a npm script? The only thing that seems like it would work would be for e.g. micro:
node-dev -r ts-node/register ./node_modules/micro-dev/bin/micro-dev.js ./src/index.ts
which is kind of a mouthful from the Redundant Department of Redundancy Department and seems to obviate the point of having those launcher scripts. (It also won't work if the runner spawns other Node processes, but that's not a problem I'm actually having.) I'd like to not have to duplicate what the launcher scripts are already doing. I'm also aware of npx having --node-arg but npx is a whole another can of worms. (On Windows it's five seconds of startup time and one spurious error message just to run a script I already have installed; it also won't find an already installed package if it can't find its .cmd launcher script, e.g. when using Docker to run the dev environment. In short I'd rather not use npx for this.)
To clear up the confusion that seems to crop up in the comments: I want to override the command line parameters that affect the behaviour of the NodeJS runtime itself executing the runner script, not pass parameters to the script itself or to my code. That is, the options listed here: https://nodejs.org/api/cli.html
One option is to write a little wrapper script that uses the current process execPath to run child_process.execFile.
So the sample here is to be able to do
node --expose-http2 --zero-fill-buffers -r ./some-module.js ./test.js
but not actually write that out, instead have wrap.js inject the args:
node ./wrap.js ./test.js
I tested running this via npm in a package.json, and it works fine. I tested that it was working by having some-module.js stick a value on the global object, and then logging it in test.js.
Files involved:
wrap.js
const child_process = require('child_process');
const nodeArgs = ['--expose-http2', '--zero-fill-buffers', '-r', './some-module.js'];
const runTarget = process.argv[2];
console.log('going to wrap', runTarget, 'with', nodeArgs);
const finalArgs = nodeArgs.concat(runTarget).concat(process.argv.slice(2));
const child = child_process.execFile(
process.execPath,
finalArgs,
{
env: process.env,
cwd: process.cwd(),
stdio: 'inherit'
}, (e, stdout, stderr) => {
console.log('process completed');
if (e) {
process.emit('uncaughtException', e);
}
});
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
and
some-module.js
global.testval = 2;
and
test.js
console.log('hi guys, did the wrap work?', global.testval)
EDIT: So upon further thought, this solution really only satisfies wrapping the initial runner. But most tools, such as mocha re-spawn a sub process which would then lose this effect. To really get the job done, you can proxy each of the child process calls and somewhat enforce that calls to spawn and such also include your args.
I rewrote the code to reflect this. Here's a new setup:
package.json
{
"scripts": {
"test": "node -r ./ensure-wrapped.js node_modules/mocha/$(npm view mocha bin.mocha) ./test.js"
},
"dependencies": {
"mocha": "^5.1.0"
}
}
ensure-wrapped.js
const child_process = require('child_process');
// up here we can require code or do whatever we want;
global.testvalue = 'hi there'
const customParams = ['--zero-fill-buffers'];
// the code below injects itself into any child process's spawn/fork/exec calls
// so that it propogates
const matchNodeRe = /((:?\s|^|\/)node(:?(:?\.exe)|(:?\.js)|(:?\s+)|$))/;
const ensureWrappedLocation = __filename;
const injectArgsAndAddToParamsIfPathMatchesNode = (cmd, args, params) => {
params.unshift(...customParams);
params.unshift(args);
if (!Array.isArray(args)) { // all child_proc functions do [] optionally, then other params
args = []
params.unshift(args);
}
if (!matchNodeRe.test(cmd)) {
return params;
}
args.unshift(ensureWrappedLocation);
args.unshift('-r');
return params;
}
child_process._exec = child_process.exec;
child_process.exec = (cmd, ...params) => {
// replace node.js node.exe or /path/to/node to inject -r ensure-wrapped.js ...args..
// leaves alone exec if it isn't calling node
cmd = cmd.replace(matchNodeRe, '$1 -r ' + ensureWrappedLocation + ' ');
return child_process._exec(cmd, ...params)
}
child_process._execFile = child_process.execFile;
child_process.execFile = (path, args, ...params) => {
params = injectArgsAndAddToParamsIfPathMatchesNode(path, args, params);
return child_process._execFile(path, ...params)
}
child_process._execFileSync = child_process.execFileSync;
child_process.execFileSync = (path, args, ...params) => {
params = injectArgsAndAddToParamsIfPathMatchesNode(path, args, params);
return child_process._execFileSync(path, ...params);
}
child_process._execSync = child_process.execSync;
child_process.execSync = (cmd, ...params) => {
cmd = cmd.replace(matchNodeRe, '$1 -r ' + ensureWrappedLocation + ' ');
return child_process._exec(bin, ...args)
}
child_process._fork = child_process.fork;
child_process.fork = (module, args, ...params) => {
params = injectArgsAndAddToParamsIfPathMatchesNode(process.execPath, args, params);
return child_process._fork(module, ...params);
}
child_process._spawn = child_process.spawn;
child_process.spawn = (cmd, args, ...params) => {
params = injectArgsAndAddToParamsIfPathMatchesNode(cmd, args, params);
return child_process._spawn(cmd, ...params)
}
child_process._spawnSync = child_process.spawnSync;
child_process.spawnSync = (cmd, args, ...params) => {
params = injectArgsAndAddToParamsIfPathMatchesNode(cmd, args, params);
return child_process._spawnSync(cmd, ...params);
}
test.js
describe('test', () => {
it('should have the global value pulled in by some-module.js', (done) => {
if (global.testvalue !== 'hi there') {
done(new Error('test value was not globally set'))
}
return done();
})
})
Please never put code like this into a node module that's published. modifying the global library functions is pretty bad.
Everything passed in the command line AFTER your nodejs application is parsed into an array called process.argv. So...
node myapp.js foo bar hello 5000
In your nodejs code...
const args = process.argv;
console.log(args[0]);
console.log(args[1]);
console.log(args[2]);
console.log(args[3]);
would yield...
foo
bar
hello
5000
I didnt get clear scenario of your problem,but as your question title ,we can execute the any cmd command from nodejs using npm libraries like:
import Promise from 'bluebird'
import cmd from 'node-cmd'
const getAsync = Promise.promisify(cmd.get, { multiArgs: true, context: cmd })
getAsync('node -v').then(data => {
console.log('cmd data', data)
}).catch(err => {
console.log('cmd err', err)
})

how to run node shelljs in sync mode and get stdout and stderr

Within a nodejs script I have the following code which makes the call synchronously and returns the stdout from the shell script I am calling:
var sh = require('shelljs');
... some code
var output = sh.exec('./someshellscript.sh', {silent:true}).stdout;
console.log(output);
... some more code (that shouldnt run until the script is complete)
I can also run the following script which will instead return the stderr:
var sh = require('shelljs');
... some code
var output = sh.exec('./someshellscript.sh', {silent:true}).stderr;
console.log(output);
... some more code (that shouldnt run until the script is complete)
However I want to receive both stdout and stderr in a sync call. Its probably something pretty obvious I am missing herebut I cant work it out.
I think you used to be able run the following command in previous versions but this just returns undefined now:
var sh = require('shelljs');
... some code
var output = sh.exec('./someshellscript.sh', {silent:true}).output;
console.log(output);
... some more code (that shouldnt run until the script is complete)
Relevant software versions are:
Ubuntu: 14.04.3 LTS
node: 4.4.4
npm: 2.15.1
shelljs: 0.7.0
Any help appreciated thanks.
From the README for the method exec(command [, options] [, callback])
Executes the given command synchronously, unless otherwise specified. [...], returns an object of the form { code:..., stdout:... , stderr:... }).
Therefore
const { stdout, stderr, code } = sh.exec('./someshellscript.sh', { silent: true })

Node JS - read file properties

I'm developing a Desktop Application with NWJS and I need to get the file properties of an .exe File.
I've tried using the npm properties module https://github.com/gagle/node-properties, but I get an empty Object.
properties.parse('./unzipped/File.exe', { path: true }, function (err, obj) {
if (err) {
console.log(err);
}
console.log(obj);
});
I need to get the "File version" Property:
I've also tried using fs.stats and no luck.
Any ideas?
Unless you want to write some native C module, there is hacky way to get this done easily: using windows wmic command. This is the command to get version (found by googling):
wmic datafile where name='c:\\windows\\system32\\notepad.exe' get Version
so you can just run this command in node to get the job done:
var exec = require('child_process').exec
exec('wmic datafile where name="c:\\\\windows\\\\system32\\\\notepad.exe" get Version', function(err,stdout, stderr){
if(!err){
console.log(stdout)// parse this string for version
}
});
If you want the properties provided as an object, you can use get-file-properties. It uses wmic under the hood, but takes care of parsing the output into an easy to use typed object for your application's consumption.
import { getFileProperties, WmicDataObject } from 'get-file-properties'
async function demo() {
// Make sure to use double backslashes in your file path
const metadata: WmicDataObject = await getFileProperties('C:\\path\\to\\file.txt')
console.log(metadata.Version)
}
Disclaimer: I am the author of get-file-properties

Resources