Managing NPM packages via a script - node.js

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.

Related

Is there a way to use custom arguments for npm install when deploying in Clever Clouds?

I'm working on a NextJS application which is deployed through Clever Clouds.
One of the newer dependency require the argument --legacy-peer-deps to be passed with npm install.
When deploying to Clever Cloud, the log only shows the command npm install --no-progress, and then fail because of this dependency.
I have tried putting the command in custom environment variable : CC_NODE_BUILD_TOOL or CC_CUSTOM_BUILD_TOOL but the deployment fail the same way.
I have tried putting it in the scripts.install, but this cause the install to recursively call itself each time it is finished.
Is there really no way to circumvent this ? Is there a way to pass the "strict-peer-deps" of npm to false ?
You can use a HOOK to run this command after the automatic install step, you can find the list of available hooks here: https://www.clever-cloud.com/doc/develop/build-hooks/.
I advise you to use the CC_POST_BUILD_HOOK environment variable to do this.

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.

require.resolve not resolving dependency

I was working on a node module, where I would have to check if the user had installed an specific package (that's the logic I have to follow) and if not, install that using child_process.exec.
That's where the problem appears, even though my script installed the package (node_modules contains it, also package.json), if I call require.resolve again, it says the module does not exist.
For what I found (from node github issues), node has sort of a "cache", where on the first call, if the package is not installed, the cache entry for this package is set to a random value which is not the path to it. Then when you install and try to call that again, it calls that cache entry.
Is there any other way to check if packages are installed, or my local node installation is broken?
Take a look into node.js require() cache - possible to invalidate?
It's possible to clean up the require cache, with something like this:
delete require.cache[require.resolve('./module.js')]
One way to check if a package is installed is trying to require it. Please see my code below that verifies if a package is not installed. Just add your code that installs it after the comment now install the missing package.
try {
require('non-existent-package');
} catch (error) {
// return early if the error is not what we're looking for
if (error.code != 'MODULE_NOT_FOUND') {
throw error;
}
// now install the missing package
}
Let me know if this works.

How can I express alternative dependencies in NPM?

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.

Detecting node-webkit during npm install

I'm working on a library for node.js that is build primarily as a native module. As such, when people try to include it in node-webkit projects, they have to recompile it using nw-gyp instead of node-gyp. I know we can detect node-webkit specifically when our built code runs using something like this:
try {
isNodeWebkit = (typeof require('nw.gui') !== "undefined");
} catch(e) {
isNodeWebkit = false;
}
However, I would like to detect this inside of our install script (run by npm install). Alternatively, we look in our own package.json, but is there maybe a way to look at the root project's package.json? That way we could at least look at some property, maybe engine or something?
In order to look into your own package.json you can do something like this
gui = require(nw.gui);
myPackageJSONFile = gui.App.manifest; // this will get the package.json file
I hope this helps.
I ended up writing a module to do this: https://github.com/maxkorp/which-native-nodish (also supports atom-shell and the renamed nw.js)
The gist is that you start at the parent directory to the module, and keep going up as long as you are a child of a node_modules folder which is a child of a folder with a package.json. Once at the root level, check the engines property in the package.json for an atom-shell, node-webkit or nwjs property. Not guaranteed to work (The farthest ancestor project must specify if its using a node-ish engine in this way), but it's better than nothing, and the only out of the box solution I've seen.
Simply as this:
isNodeWebkit = (typeof process == "object");

Resources