Prevent NodeJS npm from printing details when retrieving package - node.js

Using the node npm module (https://www.npmjs.org/api/npm.html), I want to retrieve a package but NOT have it print the results to console. Setting loglevel silent does not seem to help.
var npm = require('npm');
npm.load(function (er, npm) {
// use the npm object, now that it's loaded.
npm.config.set('loglevel', 'silent');
npm.commands.view(["<package>#<version>"],function(err, npmBody) {
//at this point it is always logging npmBody contents to console!
});
});

This can't be done without hijacking stdout. See: https://github.com/npm/npm/blob/master/lib/install.js#L100
This was fixed once, but then reverted. It appears that npm is not designed to be use programmatically.
By "hijacking stdout" I mean something like this:
console.log('Begin');
console._stdout = { write : function () {} };
externalFn();
console._stdout = process.stdout;
console.log('End');
function externalFn () {
console.log('Annoying stuff');
}

Related

Commander throws error for command with description

Here is a simple example of adding command in nodejs using commander:
'use strict';
const {Command} = require('commander');
const run = () => {
const program = new Command();
console.log('CMD');
program.command('cmd [opts...]')
.action((opts) => {
console.log('OPTS');
});
program.parse(process.argv);
};
run();
In this case everything works fine, but when I'm adding description and options, commander throws an error:
program.command('cmd [opts...]', 'DESCRIPTION', {isDefault: true})
node test-commander.js cmd opts
test-commander-cmd(1) does not exist, try --help
My env:
node v8.9.3
npm 5.3.0
commander 2.12.2
That is the declared behavior of commander. From the npm page under Git-style sub-commands...
When .command() is invoked with a description argument, no .action(callback) should be called to handle sub-commands, otherwise there will be an error. This tells commander that you're going to use separate executables for sub-commands, much like git(1) and other popular tools.
The commander will try to search the executables in the directory of the entry script (like ./examples/pm) with the name program-command, like pm-install, pm-search.
So, when you add a description like you have, it'll assume you have another executable file called test-commander-cmd for the sub command.
If commander's behavior is not what you were expecting, I might recommend looking into a package I published, called wily-cli... only if you're not committed to commander, of course ;)
Assuming your code rests in file.js, your example with wily-cli would look like this...
const cli = require('wily-cli');
const run = () => {
cli
.command('cmd [opts...]', 'DESCRIPTION', (options, parameters) => { console.log(parameters.opts); })
.defaultCommand('cmd');
};
run();
// "node file.js option1 option2" will output "[ 'option1', 'option2' ]"

Create resilient require mechanism in Node.js

I have an application and I want it to be resilient against missing dependencies. Some sort of fallback mechanism, in case a dependency is missing or corrupted.
So I have this, which monkey-patches require() and in theory will re-install a dependency if it cannot be loaded the first time:
const cp = require('child_process');
const Mod = require('module');
const req = Mod.prototype.require;
Mod.prototype.require = function (d) {
try {
return req.apply(this, arguments);
}
catch (e) {
if (/^[A-Za-z]/.test(String(d))) {
console.error(`could not require dependency "${d}".`);
try {
var actualDep = String(d).split('/')[0];
console.error(`but we are resilient and we are attempting to install "${actualDep}" now.`);
cp.execSync(`cd ${_suman.projectRoot} && npm install ${actualDep}`);
return req.apply(this, arguments);
}
catch (err) {
console.error('\n', err.stack || err, '\n');
throw e;
}
}
else {
throw e;
}
}
};
but the above patch is not working as expected. The problem is that the initial try statement:
return req.apply(this, arguments);
it mostly works, but it's doing some strange things..for example, it starts installing
bufferutil
utf-8-validate
this doesn't make sense to me, because require() works without any errors normally, but when using the patch, require() suddenly fails.
Anyone know what might be going on? Super weird.
(Note what it's doing is only attempting to re-install dependencies that start with a character [a-zA-z], it won't attempt to install a dependency that starts with './' etc.)

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.

Node JS - read file properties

I'm developing a Desktop Application with NWJS and I need to get the file properties of an .exe File.
I've tried using the npm properties module https://github.com/gagle/node-properties, but I get an empty Object.
properties.parse('./unzipped/File.exe', { path: true }, function (err, obj) {
if (err) {
console.log(err);
}
console.log(obj);
});
I need to get the "File version" Property:
I've also tried using fs.stats and no luck.
Any ideas?
Unless you want to write some native C module, there is hacky way to get this done easily: using windows wmic command. This is the command to get version (found by googling):
wmic datafile where name='c:\\windows\\system32\\notepad.exe' get Version
so you can just run this command in node to get the job done:
var exec = require('child_process').exec
exec('wmic datafile where name="c:\\\\windows\\\\system32\\\\notepad.exe" get Version', function(err,stdout, stderr){
if(!err){
console.log(stdout)// parse this string for version
}
});
If you want the properties provided as an object, you can use get-file-properties. It uses wmic under the hood, but takes care of parsing the output into an easy to use typed object for your application's consumption.
import { getFileProperties, WmicDataObject } from 'get-file-properties'
async function demo() {
// Make sure to use double backslashes in your file path
const metadata: WmicDataObject = await getFileProperties('C:\\path\\to\\file.txt')
console.log(metadata.Version)
}
Disclaimer: I am the author of get-file-properties

Is it possible to run the grunt command after a yeoman generator install?

Basically I'd like to run grunt after my generator finishes installing dependencies, I found that you can add a callback function to the installDependencies method to run after everything has been installed like this:
this.on('end', function () {
this.installDependencies({
skipInstall: options['skip-install'],
callback: function () {
console.log('All done!');
}
});
});
However I'm not sure how to run the grunt task (as in going to the terminal and running "grunt")
After this.on('end') add this lines
// Now you can bind to the dependencies installed event
this.on('dependenciesInstalled', function() {
this.spawnCommand('grunt', ['build']);
});
check this topic for more details.
But if you're using the latest update of yeomen, you'll need to make it like this
this.on('end', function () {
if (!this.options['skip-install']) {
this.npmInstall();
this.spawnCommand('grunt', ['prepare']); // change 'prepare' with your task.
}
});

Resources