how to install different files for different platforms - node.js

I'm building a command line tool using node/javascript, and want to make it available as an npm module. The tool requires a wrapping shell / windows batch script, but how do I install different scripts for different platforms? In package.json I have
"bin" : {
"lookup-bat" : "./bin/lookup.bat",
"lookup-sh" : "./bin/lookup.sh"
}
but I would like to have the same command name, regardless of platform. Is this possible?

Usually you don t mind about that.
Usually, ./bin/lookup is a js file starting with the appropriate shebang #!/usr/bin/env node.
NPM does the rest.
If the program behavior needs to be different for the runtime OS, you shall implement a sort of if(process.platform.match(/win/)) within your program.

Related

Possible way to create a command line tool in node.js

I am building a nodejs based small framework for my application. Say, the name of the framework is coffee-cup, situated in the folder of the same name. The folder has a structure inside. To simplify the work of developers, I want to add few command line methods. For example, consider following folder structure.
coffee-cup
|--config
|--cups
|--cup1
|--index.js
|--report
|--cup-config
|--cup2
|--index.js
|--report
|--cup-config
We see that inside cup1 and cup2, there is same structure. If a developer wants to add a new cup called cup3, they will need to create a folder called cup3 and add the underlying structure into that folder.
To automate this task, I am thinking of a way to build a command line utility like
$ coffee-cup new cup cup3
that can work on linux, windows or mac terminals.
Expected result of Entering this command in terminal: would create a new folder called cup3 in cups folder and will create the file index.js and folders reporting and cup-config inside the folder cup3.
I can create folder using fs.mkdirSync()and file using fs.writeFile().
I am however, unable to find a way to make the terminal identify the coffee-cup command. If terminal identifies this command, it should collect the following arguments and pass on to the nodejs file and I can continue from there. What I know is, that there exist npm packages like inquire which are useful in creating command line applications. But it does not help in building command line utilities like I mentioned above.
So I am looking for guidance on making terminal identify the coffee-cup command, collect the following arguments and pass on everything to a nodejs file. It is just like we install npm and then terminal starts identifying the npm keyword and subsequent commands like $ npm install. Any help by experts will be highly appreciated.

What to put for NPM scripts when developers are on multiple systems?

I have a web app project, and it uses Mongo, so one of my NPM scripts is "start-mongo-dev": "mongod", for spinning up the Mongo daemon during development.
I used to use OSX, but just got a new computer and run Linux Ubuntu. Now the command to begin the daemon is sudo service mongod start, so it would seem I should change the NPM script to that.
But that is a red flag. What if I go back to the old computer at all? What if I collaborate on this project with someone who uses OSX?
In short, how is one expected to handle developing with multiple OS's?
Ian's solution may be better but one alternative would be to create a bash script inside your project that detects the OS and runs the appropriate command. You can find more info on how to do that here How to detect the OS from a Bash script?
In some packages, I see divisions like this indicated with a :. For example:
"scripts": {
"start-mongo-dev:osx": "mongod",
"start-mongo-dev:ubuntu": "sudo service mongod start"
}
I am not aware of a canonical solution, but that doesn't mean there isn't one. I happen to like this one, though. #opinion

Nodejs terminal script problematic cygwin paths of arguments - cannot make an universal script

I have an issue with using node.js as a script language in the Cygwin terminal. The script is used not only on Cygwin, but also on other platforms like OS X or GNU/Linux (no issue there).
Local or absolute path arguments to the script are passed as Cygwin paths (example: /cygdrive/c/Users/username/Dir/some-file.txt), but node requires Windows path (example: C:\Users\username\Dir\some-file.txt).
There is a tool cygpath that is used to translate between the two, but because of the multiplatform requirement, I cannot hardcode them. I would not like to make any bash wrappers so I can keep it a one file script.
For example this would work: ./script $(cygpath -w /tmp/file), but I cannot require the users of the script to put cygpath every time they want to use it.
Node is installed as the windows binary and is invoked with a shabang: #!/usr/bin/env node. which nodejs yields /cygdrive/c/Program Files/nodejs/node.
TL;DR: is there any sane and portable way to trigger automatic path translation in Cygwin for arguments of a nodejs script?

node_modules/.bin/package.cmd file in node js

I am new to node js. I am trying to build a npm module and confused a bit with cmd file present in /node_modules/.bin folder with the name of the package locally.
I installed multiple packages as dependencies and found that cmd files are different.
jade.cmd
#IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\jade\bin\jade" %*
) ELSE (
#SETLOCAL
#SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\jade\bin\jade" %*
)
mocha-casperjs.cmd
#IF EXIST "%~dp0\/bin/sh.exe" (
"%~dp0\/bin/sh.exe" "%~dp0\..\mocha-casperjs\bin\mocha-casperjs" %*
) ELSE (
#SETLOCAL
#SET PATHEXT=%PATHEXT:;.JS;=;%
/bin/sh "%~dp0\..\mocha-casperjs\bin\mocha-casperjs" %*
)
My question is, if it is auto-generated by NPM, why npm creates 2 different files for 2 different packages. Is is something a user creates and tells NPM?
tl;dr
On Windows, npm creates the wrapper batch file (*.cmd) based on whatever shell/interpreter is specified in the script file's shebang line.
What you're seeing is npm's emulation of the Unix shebang mechanism for Windows, where the very 1st line of an executable plain-text file (script), if it starts with magic characters #!, tells the OS what interpreter/shell to pass the script to for execution (see this answer of mine for more information).
Since Windows doesn't support shebang lines, npm creates a wrapper *.cmd (batch) file, that explicitly invokes the "binary" file (the script file specified in the bin key of package.json) with whatever executable is specified in the script file's shebang line.
In other words: npm parses the script's shebang line to determine what shell/interpreter to invoke, and creates the wrapper batch script accordingly.
If we peek at ./bin/jade.js inside the jade package, we see #!/usr/bin/env node as the shebang line, which is why the jade.cmd wrapper file invokes node.exe.
This is the typical case: a script written in JavaScript that must be executed by the Node.js engine.
However, any other shell/interpreter may be specified too, but doing so only makes sense if a given shell/interpreter is also available on Windows; for node, that is a given, but, as the contents of mocha-casper.cmd shows, it makes no sense with the Unix default shell, /bin/sh (the ./bin/mocha-casperjs file from package mocha-casper.js contains shebang line #!/bin/sh).
The makers of the mocha-casperjs have chosen to roll their own Windows integration by re-implementing the Unix shell script for cmd.exe as mocha-casperjs.bat (also a batch file) - however, since npm is unaware of this, this batch file is not placed in the PATH (global) / not discoverable by name only (when calling CLIs in a project context).
More generally, for scripts to also work on Windows, it doesn't make sense to use shebang lines with absolute, POSIX-style paths - EXCEPT if the target shell/interpreter is specified by indirect invocation via /usr/bin/env, which signals to npm that it should be looked for in the PATH (again, see this answer of mine for more information).
(Additionally, the wrapper batch files also look in their own directory for the executable, but that is rarely helpful, given that npm wouldn't copy them there even if you explicitly added them as an entry in your package.json's bin key.)
As an aside: recent versions of npm also create extension-less wrapper scripts for Unix environments, notably Cygwin, and Bash on Ubuntu on Windows.
When creating your own packages, for CLIs ("binaries") - directly executable scripts that act like command-line utilities - that are part of an npm package to work on Windows too, you must:
Add a shebang line to your scripts - even if you only ever intend to run them on Windows.
Define that shebang line as #!/usr/bin/env <interpreter-executable-filename-without-extension>; typically - if your script is written in JavaScript and must be executed with node - use:
#!/usr/bin/env node
In your package.json file's bin key, define the script's key without extension., because .cmd is directly appended to the key name when the wrapper batch file is created (on Unix, a symlink is created named for the key as-is); e.g.:
"bin": { "foo": "./bin/fooj.js" }
Caveat: If the script pointed to by the bin key in package.json has extension .js but has no shebang line, the wrapper script will invoke the .js file directly, which will by default execute it with WSH (specifically, with JScript, its JavaScript engine) rather than Node.js, which won't work as intended - see this question for what symptoms you would see.
You can tell npm to make executable file provided with your package accessible from ./node_modules/.bin directory (in case of local install) or globally (when module is installed globally). You should place bin field into package.json and specify relative path to script. For example, jade package.json contains the following code:
"bin": {
"jade": "./bin/jade.js"
}
When installing jade package, npm makes this script (./bin/jade.js) accessible by generating, if neccessary, wrapper script (jade.cmd) which contents depend on current OS, shell, and the type of script you wish to make accessble. Jade uses .js script, and npm generates jade.cmd for your OS that will launch node and pass script name as argument. But mocha-casperjs uses shell scripts, so contents of generated mocha-casperjs.cmd are different — it starts sh.exe instead of node.
You can read about bin field of package.json here: https://docs.npmjs.com/files/package.json#bin

How to execute global npm modules via command line

I'm made a package that is meant to be installed globally and run via the command line. n is an example of a package that does this, though it isn't an actual nodejs script, but a bash script. I was considering aliases but that limits my support to the os's whose various aliasing methods I implement. Is there a better, more universal way to do that that I am missing, or am I forced to insert alias-generating functions for each supported system?

Resources