how to write a Node.js file that can be called fro

2019-08-01 17:49发布

I want to write a Node.js source file (myCode.js) in which there is a function say foo(arg1, arg2,..), such that I can ..

$ node ./myCode.js arg1 arg2

as well as

// myOtherCode.js
var foo = require('./mycode')

Is it possible? Currently I have a small bunch of files like these and they all are coded like

return (
    function foo() {
        //...
    }
)()

which I call from the node command line like

$ node ./myCode.js arg1 agr2

But now I want to string them together in a single javascript that will be called from commandline with 'node' or 'npm script' (so all functionality from different files will need to be 'require()d'). But I also want to retain the ability to run them individually if it need to be.

I understand that when called from shell prompt, the arg1, arg2 will become argv[2], argv[3] and so on, and when 'require()'d, they would be function arguments.

This probably also means that I need to pass arguments to 'require()'? How can I accomplish this?

[Using Node.js 8.x [LTS] version, so cannot use export/import syntax]

2条回答
干净又极端
2楼-- · 2019-08-01 18:02

Yes it's possible . you have to use process.argv to retreive the passed parameters

for example if we need to console the string "hello" we have to create a file "fileName.js" that contain this code :

 let firstArgument = process.argv[_.indexOf(process.argv, '--firstArgument') + 1];
 console.log(firstArgument);

and then execute this in the terminal

 node ./fileName.js --firstArgument varValue
查看更多
兄弟一词,经得起流年.
3楼-- · 2019-08-01 18:09

It's conventional to have different entry points for command-line and programmatic package use. A usual recipe for parsing CLI arguments in Node.js is process.argv.slice(2), since node and script name are the first 2 arguments.

For package-level executable script, foo function is exported from index.js entry point:

module.exports = function foo(arg1, arg2) { /* ... */ };

Considering that foo expects string arguments, bin/index.js CLI executable script can be as concise as:

require('..')(...process.argv.slice(2));

Both can be specified in package.json:

"main": "index.js",
"bin": { "foo": "bin/index.js" }

This probably also means that I need to pass arguments to 'require()'? How can I accomplish this?

There is no way to pass them to require. If a script needs to accept arguments programmatically, this means that it should be wrapped with factory function that accepts arguments.

A quick-and-dirty fix is to pass arguments to CLI executable script through process.argv. For instance, a helper function that allows to invoke executable scripts with dynamic arguments:

const decache = require('decache');

function loadCliModule(name, ...args) {
    decache(name);
    const originalArgv = process.argv;
    process.argv = [...process.argv.slice(2), ...args];
    const binModule = require(name);
    process.argv = originalArgv;
    return binModule;
}

Which can be used like:

const foo = invokeCliModule(require.resolve('./foo'), 'bar'); // foo expects process.argv[2]

Notice that due to how decache works, it decaches only the module that was loaded. If submodules make use of process.argv, this should be handled accordingly.

This shouldn't be done in production because the approach is hacky but it's perfectly suitable for testing.

A natural limitation is that arguments that are passed from CLI are strings, so a function should contain boilerplate code to convert them to expected data types (numbers, booleans, etc). For this reason it's preferable to separate programmatic and CLI entry points.

If there's a necessity to call a lot of scripts like that, this may be actual problem, and the solution is to create single CLI executable script that processes arguments with a library like yargs and calls functions from other files based on that.

查看更多
登录 后发表回答