Skip to content

karfau/runex

Repository files navigation

npm dependencies status codecov - tap --100 is part of CI

runex

Run module export as a node or npx script.

(See Why not ... for alternative approaches.)

When to use

So you have some code that you want to be able to run from the command line. You can of course just write it down into a file and run it with node ./script.js. Maybe you go one more step and add a hashbang and make it executable, so on a linux shell you run it with just ./script.js. But this way you can not import the file without executing all the code. Wrapping all the code into a function and executing it if (require.main === module) helps with that. You also manage to parse those arguments you need, maybe using one of the available libraries.

  • Are you able to also call your function from code with those arguments?
  • Do you need to make any async call (like making a request to an API)?
  • What about error handling: try {...} catch (err) {...} or .then(...).catch(...)?
  • Do you have/need one script or many scripts?

Don't regret, runex was created, because this was just to much time and code for something that convenient.

Goals

  1. reduce the amount of code that needs to be written to run a module (a javascript file) as a node script
  2. the module does not (need to) have any special code to make it executable, including support for async functions/promises
  3. convention over configuration

How to take advantage

As soon as your module exports a method with the name run, it is "runnable":

Usage: [npx] runex [options] runnable [args]

Options:
  -r, --require <module>  0..n modules for node to require (default: [])
  -h, --help              output usage information
  • it receives (just the relevant) arguments (as strings)
  • it can be async / return a Promise
  • it can throw (rejected Promises will be treated the same way)
  • it is in control of stdout (runex only communicates over stderr), with one exception:
    • if you return a value it will be printed to stdout (so you don't have to, see goal) if typeof value === 'object it will be JSON.stringifyed.

Go check some examples.

But I want my module to be executable

Of course your can make use of runex to make your module "executable"

if (require.main === module) {
  require('runex').run(module);
}

The above code would make the following differences:

  • users of your module can write node [path/to/]module instead of node runex [path/to/]module
  • you are able to customize option parsing

Why not ...

  • https://github.com/DVLP/run-func:
    This library was my initial inspiration when searching for an existing solution. But I think not every function is easy to run as a script (just think about the type of the arguments, return types, etc.). So I decided to rely on a naming convention for the executable function.

  • if (require.main === module) {...}:
    The "native" way of making a module "executable".
    But the tricky part is what needs to be written instead of ..., in every "executable" module.
    That's why I listed the first first goal.

BTW: runex makes use of this mechanism, so you don't need to, but you can of course have both.

Do you know an alternative that should be listed? Create an issue or PR!