NPM list Command
The list (or ls for short) command lists the packages and can be called using the child_process.exec command. This function does this and starts examining the packages:function npmls() {
var exec = require('child_process').exec;
// In node you can use --long switch to show description but it causes maxBuffer exceeded on parse
var child = exec('npm ls --json',
function (error, stdout, stderr) {
var tree = JSON.parse(stdout);
npminfo(tree, '');
setNpmHttp(dequeuePackage);
if (error !== null) {
console.log('exec error: ' + error);
}
});
}
The JSON looks like this if we log it:
{
"problems": [
"invalid: msnodesql@0.2.1 D:\\home\\site\\wwwroot\\node_modules\\sqlserver"
],
"dependencies": {
"apn": {
"version": "1.3.8",
"dependencies": {
"q": {
"version": "0.9.6"
}
}
},
"azure": {
"version": "0.6.7-zumo",
"from": "https://github.com/WindowsAzure/azure-sdk-for-node/tarball/v0.6.7-zumo",
"dependencies": {
"azure": {
"version": "0.7.15",
"from": "git://github.com/WindowsAzure/azure-sdk-for-node.git#v0.7.15-August2013",
"dependencies": {
"xml2js": {
"version": "0.4.0",
"dependencies": {
"sax": {
"version": "0.5.5"
}
}
},
"request": {
"version": "2.25.0",
"dependencies": {
"qs": {
"version": "0.6.5"
},
"json-stringify-safe": {
"version": "5.0.0"
},
"forever-agent": {
"version": "0.5.0"
},
"tunnel-agent": {
"version": "0.3.0"
},
"http-signature": {
"version": "0.10.0",
"dependencies": {
"assert-plus": {
"version": "0.1.2"
},
HTTPS Certificate Errors
This method was needed to make the npm view GET use http instead of https due to certificate trust issues:// Used http instead of https to stop certificate errors
function setNpmHttp (callback) {
var exec = require('child_process').exec;
var child = exec('npm config set registry="http://registry.npmjs.org/"',
function (error, stdout, stderr) {
callback();
});
}
NPM Info
Once we've parsed the JSON, we can start examining each package and it's dependencies. This function walks the dependencies and builds an array of objects used for processing the package information:// Recursive function for getting package info for all dependencies
function npminfo(node, depth) {
var dp = node.dependencies;
if(dp !== null) {
for (var key in dp) {
if (dp.hasOwnProperty(key)) {
// Build info
var link = 'https://www.npmjs.org/package/' + key;
var info = depth + '<a href=\'' + link + '\', \'' + key + '\' onclick="window.open(this.href, \'' + key + '\' ); return false" target="_blank">' + key + '</a>' + ' V' + dp[key].version;
packages.push({ 'key' : key, 'info' : info, 'depth' : depth.length })
// Loop through dependencies
npminfo(dp[key], depth + '-');
}
}
}
}
Extra Information
Once we have a list of package objects, we can process them to get extra description information:function dequeuePackage(){
// Dequeue and execute
if(packages.length > 0){
var pack = packages.shift();
if(pack.depth == 0){
writeLine();
// Get detail
var exec = require('child_process').exec;
var child = exec('npm view ' + pack.key + ' description',
function (error, stdout, stderr) {
if (error !== null) {
console.error('exec error: ' + error);
}
else{
pack.info = pack.info + ' - ' + stdout;
writeLine('<b>' + pack.info + '</b>');
}
// Do next
dequeuePackage();
});
}
else {
// Do next
writeLine(pack.info);
dequeuePackage();
}
}
else {
// We're finished
opText = opText + '</body></html>';
response.send(statusCodes.OK, opText);
}
}
This is only done for top-level packages because it take's too long to make requests for all packages and the API request will time-out!
Full Script
When we put it all together, the API method looks like this:exports.get = function(request, response) {
// Array of package info objects
var packages = [];
// html start
var opText = '<!DOCTYPE html><html><head><title>NPM Modules</title></head><body>';
opText += '<h1>Windows Azure Mobile Services</h1>';
opText += '<h2>NPM Packages and Dependencies</h2>';
// List packages
npmls();
// Adds a line with break
function writeLine(text){
if(text)
opText += text;
opText += '<br />';
}
function npmls() {
var exec = require('child_process').exec;
// In node you can use --long switch to show description but it causes maxBuffer exceeded on parse
var child = exec('npm ls --json',
function (error, stdout, stderr) {
var tree = JSON.parse(stdout);
console.log(stdout);
npminfo(tree, '');
setNpmHttp(dequeuePackage);
if (error !== null) {
console.log('exec error: ' + error);
}
});
}
// Used http instead of https to stop certificate errors
function setNpmHttp (callback) {
var exec = require('child_process').exec;
var child = exec('npm config set registry="http://registry.npmjs.org/"',
function (error, stdout, stderr) {
callback();
});
}
// Recursive function for getting package info for all dependencies
function npminfo(node, depth) {
var dp = node.dependencies;
if(dp !== null) {
for (var key in dp) {
if (dp.hasOwnProperty(key)) {
// Build info
var link = 'https://www.npmjs.org/package/' + key;
var info = depth + '<a href=\'' + link + '\', \'' + key + '\' onclick="window.open(this.href, \'' + key + '\' ); return false" target="_blank">' + key + '</a>' + ' V' + dp[key].version;
packages.push({ 'key' : key, 'info' : info, 'depth' : depth.length })
// Loop through dependencies
npminfo(dp[key], depth + '-');
}
}
}
}
function dequeuePackage(){
// Dequeue and execute
if(packages.length > 0){
var pack = packages.shift();
if(pack.depth == 0){
writeLine();
// Get detail
var exec = require('child_process').exec;
var child = exec('npm view ' + pack.key + ' description',
function (error, stdout, stderr) {
if (error !== null) {
console.error('exec error: ' + error);
}
else{
pack.info = pack.info + ' - ' + stdout;
writeLine('<b>' + pack.info + '</b>');
}
// Do next
dequeuePackage();
});
}
else {
// Do next
writeLine(pack.info);
dequeuePackage();
}
}
else {
// We're finished
opText = opText + '</body></html>';
response.send(statusCodes.OK, opText);
}
}
};
No comments:
Post a Comment