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:
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
Top comments (5)
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:
So obviously I don't completely agree with
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.