loading...

My first published npm package is called runex

karfau profile image Christian Bewernitz ・1 min read

Some months ago I got tired of all those tiny differences that you need to consider when writing CLI scripts for node.

So in the last days I took the time to rewrite a module that I have already copied into more than three repositories:

GitHub logo karfau / runex

Run (javascript) module export as a script

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…

The first version has already been published to npm:
https://npmjs.org/package/runex

So if your module exports a method named run it can now be used as a CLI tool:

npx runex path/to/file.js

I still have some ideas for features to implement, but I'm very happy it's out there now.

Merry X-Mas and Let me know what you think

Discussion

markdown guide
 

Can’t you just... you know... node path/to/file.js?

 

Of course you can. I'm going into more details on that approach in the section When to use.

&TLDR; I experienced the following situation: The more complex your requirements get and/or the more of those scripts you have, the uglier it get's if all of those scripts do all the proper handling themselves.

I was longing for to not have to think about all the differences when writing a CLI script. Just write functions like I always do and have a convention that properly takes care of the common stuff.

If you have more in dept/specific questions feel free to ask/list them here or as one or more issues. I hope I will be able to reply more in dept/specifically.

 

I checked that, and the only advantage for this is if you have commands and other stuff in the same file, which is against separation of concerns.

Do I understand you correctly:
You only ever have a things for a single concern in every file/module?

That's awesome.
And yes, in that case you might not need runex.

And of course dependencies should only be added when it makes sense.

But this level of separation of concerns wasn't the case for me and I didn't want to make that a requirement to make it simple to write CLI scripts.

Also even if I wanted to, it's out of my reach, since it would be tons of effort getting from where the code base is, to where it would need to be.

So yes, this library helps with "getting CLI done" independent of your level of separation of concern.

I still think increasing the level of separation of concerns in any code base can and should be part of the boy scout habit. And I'm regularly advocating for it.

But I'm "more" concerned about that aspect on the levels of functions then on the level of modules:

For me a module (or folder or package) is (just) a(nother) way to abstract away higher level implementation/composition details to create an API that is more convenient to use. (Every one of these different levels have slightly different ways of consumption, of course.)

I have seen many different levels of abstractions being applied to all of these levels, especially in larger/very different code bases where a lot of intermediate abstraction levels are present.

There is even tooling to allow different use cases:

  • bit.dev/ is enabling jumping from a single module to a package
  • while lerna.js.org/ allows you to have one more level called workspace and staying in the same repository

So obviously I don't completely agree with

in the same file, which is against separation of concerns.

Beside that, I'm not convinced this could be the only reason to use runex:

Just in case you would be able to configure the name of the function to execute by adding a command line argument (I'm thinking about adding this feature in the future), it would allow you to run quite some functions as a CLI without writing any extra code, just thinking about lodash... (I know this approach has some limitations, that's why it's not my focus right now.)

For code that is able to run inside node, and that is independent of the context it runs in, e.g. because it's purely functional, why should it care how it is invoked ("programmatically" vs CLI)?

Is that an idea that you can relate to / think could be useful?
Or can you think of something else that would make it useful for you?

Best regards

I guess if you added support for custom-named functions, it could be sorta useful for people who haven't yet had the time to implement a proper CLI yet, probably because getting their project running was a higher priority. While I do understand your reasoning behind runex, I personally could not see myself in the same situation.