How can I express alternative dependencies in NPM? - node.js

When expressing the dependencies of a Debian package, you can use syntax like exim | mail-transport-agent to indicate that your package needs either exim or mail-transport-agent, but it doesn't care which.
I want to express something similar in NPM. Is there a way to do it? Specifically suppose I want my application to express a dependency on either mikesthing-impl1 v1.7 better or mikesthing-impl2 v2.1 or better. I'd like to be able to say something like:
dependencies: {
"mikesthing": {
"mikesthing-impl1": "^1.7",
"mikesthing-impl2": "^2.1"
}
}
Is there a way?

No, there is no functionality within a package.json to specify that sort of logic. However, you can implement a postinstall script in the scripts that will be executed after all other dependencies have been installed and in which you can script out this kind (or any kind) of behavior.
e.g. (in package.json)
"scripts": {
"postinstall": "./bin/postinstall"
A good place to start is to run npm view {package} to get back a JSON object that details what versions are available in the registry.

Related

I need to add dependency in my package.json and load module , after taking user input while running npm install

I need to add a dependency in my package.json and load module, by taking user input in command prompt while running npm install.
Is it possible to do so.
As you haven't provided much information about what exactly you try to accomplish, it's difficult to advise you or give a concrete example. Anyhow, in package.json, you can override the default behavior of npm scripts with the scripts field:
"scripts": {
"install": "./scripts/install.sh",
}
As someone else has noted before, you cannot call npm install in your custom script, though, as this would lead to infinite recursion. Thus, rather provide a preinstall script, which npm runs before install.
For use cases other than adding dependencies based on user input, I recommend to check how to set the environment variables and configurations, which the user then could override with npm config set project-name:config-name config-value.

How can I use a custom version of a npm package in my project?

I use Primus in my project. Primus is a wrapper for websockets libraries like sockjs, which I use.
At server boot, primus creates a script, that will then be downloaded by our client, as the client part of the socket. This script embeds the source code of sockjs. Problem, it uses the 1.1.2 version of sockjs, that contains a bug, fixed in version 1.1.4, but not in Primus yet.
I asked the maintainers of Primus to change it but the are too slow to do it.
So I forked Primus, and I replaced the wrong file by the good one.
Now, I want to use this package instead of the wrong one, declared currently in my package.json.
I don't know what the good practice is in this case, the only solution I can think is to npm-publish my modified package under a different name, like amplement-primus, and then do npm install --save amplement-primus to insert it in my project. I have no idea of it breaks a rule of npm.
Do you think it's a good idea, or do you have something better in mind ?
Thank you !
I used the github url of the project to reference it in package.json:
"dependencies": {
"primus": "git+https://github.com/amplement/primus.git",
"sockjs": "^0.3.19"
},
You can use forked repo to install the package.
like :
npm install github_url_of_your_repo
For more details:
See this.

Managing NPM packages via a script

Is there a library or is it perhaps built into NPM itself to manage packages and install them within a script? I'm writing a process that checks if a local package exists and installs if it doesn't. Then I'd like to be able to dynamically require it in the same process.
This is definitely possible, but probably inadvisable.
I found the npm module "npm-programmatic" which lets you npm install. Once you have that, all you need to do is wrap your require with a try catch so that you can handle when a require fails.
const npm = require('npm-programmatic')
let myPackage
try {
myPackage = require('my-package')
} catch(err) {
npm.install(['my-package']).then(function() {
myPackage = require('my-package')
console.log(myPackage)
})
}
The biggest problem you're probably going to face here is that the script will need to be running with more than standard privileges. You will likely need to sudo run this script which is very inadvisable.
No, I think that there is no way to accomplish your aim, and in fact, it is a "bad" idea, I think.
Under Node.js, we always use package.json to manage all dependencies, and when we want to deploy them, we only use to run
$npm install
it is very easy and effective, but based on your mind, we need write a new function instead of require function, for example, require2, and while we use it to load the module, it always check the module exists or not at first, it is not effective, I think.

depcheck with Node.js - finding require('x') where x is not in package.json

I found this library called depcheck:
https://www.npmjs.com/package/depcheck
At first I just assumed that it did what I want - find modules that are used in the code but not declared in package.json, but all it does it look for unused dependencies. I am looking for the exact reverse- dependencies that are used in the code but not declared in package.json.
Is there such an NPM module that can do this? I can't find it. The reason this matters is that when deploying Node.js applicatons it's very common that we are missing a dependency in package.json that we are using our code because one of us forgot to include the --save option when running npm install x.
dependency-check looks like it does what you need.
I could not find a suitable solution to this problem so I created my own that we are using in production with Mocha testing -, I hope it might be useful to you also:
https://www.npmjs.com/package/nodejs-dep-check

Is it possible to consume environment variables inside of npm / package.json?

I'm attempting to build a package.json so that when running a NodeJS app on Heroku it will run the scripts.postinstall step using an environment variable. For example:
...
"scripts": {
"postinstall": "command $ENV_VAR"}
},
...
I've looked at the docs and wasn't able to find something saying I can.
Is this even possible? Is this even desirable and "I'm Doing It Wrong"™?
Ignore the nay-sayers. You can do this in a cross-platform manner using cross-var:
"scripts": {
"postinstall": "cross-var command $ENV_VAR"
}
Updated answer due to new packages having been written
You can use the cross-var package to do this in a clean way:
...
"scripts": {
...
"postinstall": "cross-var command $ENV_VAR",
...
},
"dependencies": {
...
"cross-var": "^1.1.0",
...
}
...
Original answer
To answer the last questions, because they're the most important one: yes, no, and absolutely, because you've just broken cross-platform compatibility. There is no guarantee your environment syntax works for all shells on all operating systems, so don't do this.
We have a guaranteed cross-platform technology available to us already: Node. So, create a file called something like bootstrap.js, and then make npm run node bootstrap as your postinstall script. Since the code inside bootstrap.js will run like any other node script, it'll have access to process.env in a fully cross-platform compatible way, and everyone will be happy.
And many, many, many things that use common utils have node equivalents, so you can npm install them, locally rather than globally, and then call them in an npm script. For instance mkdir -p is not cross-platform, but installing the mkdirp module is, and then an npm script like "ensuredirs": "mkdirp dist/assets" works fine everywhere when run as npm run ensuredirs
And for convenience, the most common unix utilities have their own runner package, shx, which is fully cross-platform and makes the lives of devs even easier, with the "if you're writing code" equivalent being fs-extra.

Resources