Get list of all NPM dependencies minimum node.js engine version - node.js

We have an NPM project X. I want to get a distinct list of all the dependencies in the project and the minimum Node.js (engine) version that they need.
How can I do this?
The motivation is of course to discovery what the minimum Nodejs version we need to run in development and production.
npm ls | grep "engines"
something like that, except the above won't work, hopefully there is something more robust

I was able to accomplish this like so:
let npm = require('npm');
npm.load({}, function(err, npm) {
if(err) throw err;
npm.config.set('global', false); // => we don't want to consider global deps
npm.commands.list([], true, function(err, pkgInfo) {
let enginesList = Object.keys(pkgInfo.dependencies).map(function(k){
return {
dep: k,
engines: pkgInfo.dependencies[k].engines || {}
}
});
enginesList.forEach(function(val){
console.log(val.dep + ' => ', val.engines);
});
});
});

Related

Loopback getting error - Major change in User validatePassword function in the same release itself (3.0.0)

I am using loopback 3.0.0, and I have set up a new server recently, about one week ago. For that, I have ran the command npm install by putting the package.son file.
But in that installed files, the node_modules/loopback/common/user.js module has changed with major changes.
Egs:
Old file:
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
User.validatePassword = function(plain) {
var err;
if (plain && typeof plain === 'string' && plain.length <= MAX_PASSWORD_LENGTH) {
return true;
}
if (plain.length > MAX_PASSWORD_LENGTH) {
err = new Error(g.f('Password too long: %s', plain));
err.code = 'PASSWORD_TOO_LONG';
} else {
err = new Error(g.f('Invalid password: %s', plain));
err.code = 'INVALID_PASSWORD';
}
err.statusCode = 422;
throw err;
};
New File:
// Copyright IBM Corp. 2014,2018. All Rights Reserved.
User.validatePassword = function(plain) {
var err;
if (!plain || typeof plain !== 'string') {
err = new Error(g.f('Invalid password.'));
err.code = 'INVALID_PASSWORD';
err.statusCode = 422;
throw err;
}
// Bcrypt only supports up to 72 bytes; the rest is silently dropped.
var len = Buffer.byteLength(plain, 'utf8');
if (len > MAX_PASSWORD_LENGTH) {
err = new Error(g.f('The password entered was too long. Max length is %d (entered %d)',
MAX_PASSWORD_LENGTH, len));
err.code = 'PASSWORD_TOO_LONG';
err.statusCode = 422;
throw err;
}
};
I have developed my code with the same version but with old code which they have provided in the same version(3.0.0.). Here you can see, in the new code there is no return statement, so the code is infinitely waiting for the return and being time out. In both places the package.json file contains the same version: "loopback": "^3.0.0"
I hope it's not recommended to copy the node_modules from our developement server to production server.
So how can we handle these type of issues?
When specifying a version number in the package.json there are a few different ways https://docs.npmjs.com/files/package.json#dependencies:
The way you have is the default, ^ which means
compatible with version
So ^3.0.0 will only install 3.0.0 if it is the latest minor and fix versions otherwise it will take whatever the latest version of loopback is on that day. Today that is 3.19.3.
The issue was introduced in version v3.10.1(thanks #vasan) so locally maybe you had version 3.10.0 then on the server you had 3.10.1
There is a good explanation about the version numbers in this question What's the difference between tilde(~) and caret(^) in package.json?
I would suggest using an exact version, i.e. 3.19.3 then using a service like rennovate, https://github.com/renovate-bot, to update your project to keep up to date with security patches
There is also another guard against this, package-lock.json https://docs.npmjs.com/files/package-lock.json introduced in version 5 of npm. If you check this file in it will make sure that wherever you run npm install the exact version of the npm module is installed wherever you run it.

What is the best way to run npm packages on demand as mircoservices without installing it locally?

Let's say I have these npm packages published to npm:
service1#v1.0
service1#v2.0
service2#v1.0
each package has a single function:
function run(extraStr) {
return 'package_name_and_version' + extraStr; // i.e. service1 v1.0 extraStr
}
And I want to write nodejs code that use the packages without installing it locally
var server = require("my-server-sdk");
// get(package_name, version, function_in_package, arguments, callback)
server.get('service1', '2.0', 'run', ['app1'], (err, result) => {
console.log(result); // this should print service1 v2.0 app1
});
where my-server-sdk is an sdk that interface with my server's api where it install the required packages and cache it for later use.
What is the best way to do that? what the security concerns and how to prevent any?
this is a simple diagram for what I want
NOTE: service1#v1.0
service1#v2.0
service2#v1.0
are just examples to any packages in npm i.e. lodash
Caching example:
Let's say we have TTL equal 60 minutes.
client1 requested a function from lodash and another function from underscore at 01:00.
Now in the server lodash and underscore are installed with timestamp 01:00.
client2 requested a function from underscore at 01:30 which get used instantly because underscore is installed before but it timestamp got updated to 1:30.
At 02:01 lodash get deleted because it didn't get used on the past TTL currenttime - lodash_timestamp > TTL but underscore stays because currenttime - underscore_timestamp < TTL
So when client3 request lodash at 02:30 it get intsalled again with 02:30 as a timestamp.
There is the npmi package which gives an API to npm install.
The logic I would use is:
Get the specific package and version from npm (install if is not already installed)
Require the package inside nodejs
Run the specified method with the specified parameters
Return the results to the client
var npmi = require('npmi');
var path = require('path');
function runModule(moduleName, moduleVersion, moduleMethod, moduleMethodParams) {
return new Promise((resolve, reject) => {
var options = {
name: moduleName, // your module name
version: moduleVersion, // expected version [default: 'latest']
forceInstall: false, // force install if set to true (even if already installed, it will do a reinstall) [default: false]
npmLoad: { // npm.load(options, callback): this is the "options" given to npm.load()
loglevel: 'silent' // [default: {loglevel: 'silent'}]
}
};
options.path = './' + options.name + "#" + options.version,
npmi(options, function(err, result) {
if (err) {
if (err.code === npmi.LOAD_ERR) console.log('npm load error');
else if (err.code === npmi.INSTALL_ERR) console.log('npm install error');
console.log(err.message);
return reject(err)
}
// installed
console.log(options.name + '#' + options.version + ' installed successfully in ' + path.resolve(options.path));
var my_module = require(path.resolve(options.path, "node_modules", options.name))
console.log("Running :", options.name + '#' + options.version)
console.log("Method :", moduleMethod);
console.log("With params :", ...moduleMethodParams)
resolve(my_module[moduleMethod](...moduleMethodParams))
});
})
}
runModule('lodash', '4.10.0', 'fill', [Array(3), 2])
.then(result => console.log("Result :", result))
runModule('lodash', '3.10.0', 'fill', [Array(3), 2])
.then(result => console.log("Result :", result))
You could see now that there are 2 created folders (lodash#3.10.0 , lodash#4.10.0) indicating the package name and version.
I have made the assumptions that npm is in path and the server has the permissions to install packages in current directory, also that the "MODULE_NAME#MODULE_VERSION" is a valid folder name under the current OS.

node require.cache delete does not result in reload of module

I'm writing tests for my npm module.
These tests require to install multiple versions of an npm module in order to check if the module will validate them as compatible or incompatible.
Somehow all uncache libraries or function I found on stackoverflow or the npm database are not working..
I install/uninstall npm modules by using my helper functions:
function _run_cmd(cmd, args) {
return new Promise((res, rej) => {
const child = spawn(cmd, args)
let resp = ''
child.stdout.on('data', function (buffer) {
resp += buffer.toString()
})
child.stdout.on('end', function() {
res(resp)
})
child.stdout.on('error', (err) => rej(err))
})
}
global.helper = {
npm: {
install: function (module) {
return _run_cmd('npm', ['install', module])
},
uninstall: function (module) {
decacheModule(module)
return _run_cmd('npm', ['uninstall', module])
}
}
}
This is my current decache function which should clear all modules caches (I tried others, including npm modules none of them worked)
function decacheModules() {
Object.keys(require.cache).forEach(function(key) {
delete require.cache[key]
})
}
I am installing multiple versions of the less module (https://www.npmjs.com/package/less)
In my first test I am installing a deprecated version which does not have a render-function.
In some other test I am installing an up-to-date version which has the render-function. Somehow if I test for that function that test does fail.
If I skip the first test the other test succeeds. (render-function exists).
This makes me believe that the deletion of require.cache has no impact...
I am using node v4.2.4.
If there is a reference to the old module:
var less = require('less');
Clear module cache will not lead that reference cleared and reload the module.
For that to work, at least you don't store module to variable, use the "require('less')" in place everywhere.

How to use npm.commands.version to bump the package.json version programatically

I'd like the package.json version of my project to bump up every time I run my project, for that I installed the npm package into my project as a local node-module but the following does not work:
var npm = require('npm');
npm.load(function (e, n) {
n.commands.version("patch", function (error) {
// 'error' here equals an error message as shown below
console.log(error);
});
});
Error message
"npm version [ | major | minor | patch | prerelease | preminor | premajor ](run in package dir)'npm -v' or 'npm --version' to print npm version (2.4.1)'npm view version' to view a package's published version'npm ls' to inspect current package/dependency versions"
Any idea what's missing and how to make it work?
Thanks!
Btw, I don't use grunt and not planning to at this point, so grunt-bump is not an option.
Well when I read the docs, I see "Like all other commands, this function takes a string array as its first parameter", which means you need ["patch"] where you have "patch":
var npm = require('npm');
npm.load(function (e, n) {
n.commands.version(["patch"], function (error) {
// 'error' here equals an error message as shown below
console.log(error);
});
});
That works fine for me.

How do I require a minimum version of node.js in my script?

I just found out that a script I wrote only works on node 0.10 because it uses readable events.
How do I require a minimum version of node.js in my script so that users know that they need to upgrade?
In package.json:
{ "engines" : { "node" : ">=0.10.3" } }
From the docs.
Edit, a programmatic way:
var pkg = require('./pacakge'),
semver = require('semver');
if(!semver.satisfies(process.version, pkg.engines.node)) {
// Not sure if throw or process.exit is best.
throw new Error('Requires a node version matching ' + pkg.engines.node);
}
Add this to top the top of your script.
var versionComps = process.versions['node'].split('.');
if (parseInt(versionComps[0]) === 0 && parseInt(versionComps[1]) < 10) {
console.log('Script requires node.js version >= 0.10');
process.exit(1);
};

Resources