Compare commits
No commits in common. "master" and "v1.0.0" have entirely different histories.
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"bracketSpacing": true,
|
|
||||||
"printWidth": 120,
|
|
||||||
"singleQuote": true,
|
|
||||||
"tabWidth": 2,
|
|
||||||
"trailingComma": "none",
|
|
||||||
"useTabs": true
|
|
||||||
}
|
|
42
README.md
42
README.md
@ -1,41 +1,3 @@
|
|||||||
# [how-npm-am-i.js](https://git.coolaj86.com/coolaj86/how-npm-am-i.js)
|
# how-npm-am-i.js
|
||||||
|
|
||||||
Shows how many downloads you get on npm each month, across all of your packages.
|
Shows how many downloads you get on npm each month, across all your packages.
|
||||||
|
|
||||||
> npx how-npm-am-i <username> [--verbose]
|
|
||||||
|
|
||||||
# Usage
|
|
||||||
|
|
||||||
```bash
|
|
||||||
how-npm-am-i isaacs --verbose
|
|
||||||
```
|
|
||||||
|
|
||||||
```txt
|
|
||||||
You've published 161 packages to npm and you get...
|
|
||||||
|
|
||||||
Package Name Downloads
|
|
||||||
@isaacs/example-automatic-publishing: 3
|
|
||||||
cluster-callresp: 11
|
|
||||||
voxer-blog-demo: 12
|
|
||||||
tako-session-token: 13
|
|
||||||
truncating-stream: 18
|
|
||||||
...
|
|
||||||
rimraf: 59,309,683
|
|
||||||
minimatch: 63,109,081
|
|
||||||
yallist: 66,527,785
|
|
||||||
lru-cache: 77,627,756
|
|
||||||
glob: 109,654,115
|
|
||||||
|
|
||||||
1,423,623,087 downloads per month. Not bad!
|
|
||||||
You're *very* npm.
|
|
||||||
```
|
|
||||||
|
|
||||||
# Install
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install --global how-npm-am-i
|
|
||||||
```
|
|
||||||
|
|
||||||
# How I built this
|
|
||||||
|
|
||||||
See <https://coolaj86.com/articles/how-cool-you-are-on-npm/>
|
|
@ -1,92 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var pkg = require('../package.json');
|
|
||||||
|
|
||||||
var args = process.argv.slice(1).join(',');
|
|
||||||
if (/-V|--version/.test(args)) {
|
|
||||||
console.info('v' + pkg.version);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var verbose = false;
|
|
||||||
if (/-v|--verbose/.test(args)) {
|
|
||||||
verbose = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var username = process.argv[2];
|
|
||||||
if (!username) {
|
|
||||||
console.error();
|
|
||||||
console.error('Usage: how-npm-am-i <username>');
|
|
||||||
console.error(' Ex: how-npm-am-i isaacs');
|
|
||||||
console.error();
|
|
||||||
process.exit(1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hnai = require('../');
|
|
||||||
hnai
|
|
||||||
.packages(username)
|
|
||||||
.then(function(pkgnames) {
|
|
||||||
console.info('');
|
|
||||||
if (!pkgnames.length) {
|
|
||||||
console.info("You've published " + pkgnames.length + ' packages to npm. Hmm.');
|
|
||||||
console.info('Well, we all have to start somewhere.... except maybe you.... you just... keep doing you. 👍');
|
|
||||||
console.info('');
|
|
||||||
process.exit(1337);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.info("You've published " + pkgnames.length + ' packages to npm and you get...');
|
|
||||||
hnai.downloads(pkgnames).then(function(downloads) {
|
|
||||||
var total = downloads.reduce(function(sum, el) {
|
|
||||||
return sum + el.downloads;
|
|
||||||
}, 0);
|
|
||||||
if (verbose) {
|
|
||||||
tabularize(downloads);
|
|
||||||
}
|
|
||||||
console.info(total.toLocaleString() + ' downloads per month. Not bad!');
|
|
||||||
if (total >= 100000 || pkgnames.length > 100) {
|
|
||||||
console.info("You're a little npm. Keep trying.");
|
|
||||||
} else {
|
|
||||||
console.info("You're *very* npm. Much wow!");
|
|
||||||
}
|
|
||||||
console.info();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(function(e) {
|
|
||||||
console.error();
|
|
||||||
console.error(e);
|
|
||||||
console.error();
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
function tabularize(downloads) {
|
|
||||||
console.info('');
|
|
||||||
var maxLen = 0;
|
|
||||||
var maxCount = "Downloads".length;
|
|
||||||
downloads.sort(function(a, b) {
|
|
||||||
maxLen = Math.max(a.package.length, b.package.length, maxLen);
|
|
||||||
maxCount = Math.max(a.downloads.toLocaleString().length, b.downloads.toLocaleString().length, maxCount);
|
|
||||||
return a.downloads - b.downloads;
|
|
||||||
});
|
|
||||||
console.info(leftpad('Package Name', maxLen + 1), rightmad('Downloads', maxCount + 1));
|
|
||||||
downloads.forEach(function(stat) {
|
|
||||||
console.info(leftpad(stat.package + ':', maxLen + 1), rightmad(stat.downloads.toLocaleString(), maxCount + 1));
|
|
||||||
});
|
|
||||||
console.info('');
|
|
||||||
}
|
|
||||||
|
|
||||||
// no, I was right
|
|
||||||
function leftpad(str, n) {
|
|
||||||
while (str.length < n) {
|
|
||||||
str = str + ' ';
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
function rightmad(str, n) {
|
|
||||||
while (str.length < n) {
|
|
||||||
str = ' ' + str;
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
115
index.js
115
index.js
@ -1,115 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
// Inspired in part by
|
|
||||||
// * https://www.npmjs.com/package/npmjs-api
|
|
||||||
// * https://github.com/kevva/npm-user-packages/blob/master/index.js
|
|
||||||
|
|
||||||
// Official docs at
|
|
||||||
// * https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#meta-endpoints
|
|
||||||
// * https://github.com/npm/registry/blob/master/docs/download-counts.md
|
|
||||||
|
|
||||||
var batchAsync = require('batchasync').batchAsync;
|
|
||||||
var request = require('@root/request');
|
|
||||||
request = require('util').promisify(request);
|
|
||||||
|
|
||||||
var hnai = exports;
|
|
||||||
hnai.packages = function(username) {
|
|
||||||
// I didn't bother to implement paging because... I didn't
|
|
||||||
return request({
|
|
||||||
url: 'https://api.npms.io/v2/search?q=author:' + username + '&size=250&from=0',
|
|
||||||
json: true
|
|
||||||
}).then(function(resp) {
|
|
||||||
// { total: 214, results: [ { package: { name: 'foobar' } } ] }
|
|
||||||
if (resp.total > 250) {
|
|
||||||
console.warn();
|
|
||||||
console.warn("You have more than 250 published packages, but I haven't implemented paging.");
|
|
||||||
console.warn("I'd say please open an issue, but you're ovbiously skilled, so please PR.");
|
|
||||||
console.warn();
|
|
||||||
}
|
|
||||||
return resp.body.results.map(function(result) {
|
|
||||||
return result.package.name;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
hnai.downloads = function(pkgnames) {
|
|
||||||
// https://github.com/npm/registry/blob/master/docs/download-counts.md#bulk-queries
|
|
||||||
// FYI: Earliest date is January 10, 2015
|
|
||||||
// * One-off: https://api.npmjs.org/downloads/range/{period}[/{package}]
|
|
||||||
// * Bulk: https://api.npmjs.org/downloads/point/last-day/npm,express
|
|
||||||
|
|
||||||
// All the batching logic
|
|
||||||
var batches = [];
|
|
||||||
var batch = [];
|
|
||||||
pkgnames.forEach(function(pkgname) {
|
|
||||||
if ('@' === pkgname[0]) {
|
|
||||||
batches.push([pkgname]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (batch.length >= 128) {
|
|
||||||
batches.push(batch);
|
|
||||||
batch = [];
|
|
||||||
}
|
|
||||||
batch.push(pkgname);
|
|
||||||
});
|
|
||||||
if (batch.length) {
|
|
||||||
batches.push(batch);
|
|
||||||
}
|
|
||||||
|
|
||||||
// do the batching
|
|
||||||
return batchAsync(4, batches, function(batch) {
|
|
||||||
if (1 === batch.length) {
|
|
||||||
return one(batch[0]);
|
|
||||||
} else {
|
|
||||||
return many(batch);
|
|
||||||
}
|
|
||||||
}).then(function(results) {
|
|
||||||
// transform the results
|
|
||||||
var all = [];
|
|
||||||
results.forEach(function(map) {
|
|
||||||
//#console.debug('batch size:', Object.keys(map).length);
|
|
||||||
Object.keys(map).forEach(function(key) {
|
|
||||||
if (!map[key]) {
|
|
||||||
// can be 'null' for just-published packages
|
|
||||||
map[key] = { downloads: 0 };
|
|
||||||
}
|
|
||||||
all.push({
|
|
||||||
package: key,
|
|
||||||
downloads: map[key].downloads
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// get back one big happy list
|
|
||||||
return all;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// these need to be simplified and transformed
|
|
||||||
function one(pkgname) {
|
|
||||||
return request({
|
|
||||||
url: 'https://api.npmjs.org/downloads/range/last-month/' + pkgname,
|
|
||||||
json: true
|
|
||||||
}).then(function(resp) {
|
|
||||||
// { package: 'foobar', downloads: [{ downloads: 1, day: '2019-06-16' }] }
|
|
||||||
var result = {};
|
|
||||||
result[resp.body.package] = {
|
|
||||||
package: resp.body.package,
|
|
||||||
downloads: resp.body.downloads.reduce(function(sum, el) {
|
|
||||||
return el.downloads;
|
|
||||||
}, 0)
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// these are exactly what we want, pretty much
|
|
||||||
function many(pkgnames) {
|
|
||||||
return request({
|
|
||||||
url: 'https://api.npmjs.org/downloads/point/last-month/' + pkgnames.join(','),
|
|
||||||
json: true
|
|
||||||
}).then(function(resp) {
|
|
||||||
// { foobar: { downloads: 12 } }
|
|
||||||
return resp.body;
|
|
||||||
});
|
|
||||||
}
|
|
18
package-lock.json
generated
18
package-lock.json
generated
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "how-npm-am-i",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"lockfileVersion": 1,
|
|
||||||
"requires": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@root/request": {
|
|
||||||
"version": "1.3.11",
|
|
||||||
"resolved": "https://registry.npmjs.org/@root/request/-/request-1.3.11.tgz",
|
|
||||||
"integrity": "sha512-3a4Eeghcjsfe6zh7EJ+ni1l8OK9Fz2wL1OjP4UCa0YdvtH39kdXB9RGWuzyNv7dZi0+Ffkc83KfH0WbPMiuJFw=="
|
|
||||||
},
|
|
||||||
"batchasync": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/batchasync/-/batchasync-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-YSKKrMr7BLsQnqHOtGKhuqC83CtHu9pGOhSrmPl8WCz7N3qcFJfEQGbM1I3PEOq11FU2rY+a+IokOODzOyVdXw=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
41
package.json
41
package.json
@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "how-npm-am-i",
|
|
||||||
"version": "1.0.5",
|
|
||||||
"description": "See how many downloads you get on npm each month, across all your packages",
|
|
||||||
"homepage": "https://git.coolaj86.com/coolaj86/how-npm-am-i.js",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "node test.js"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://git.coolaj86.com/coolaj86/how-npm-am-i.js.git"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"how-npm-am-i": "bin/how-npm-am-i.js"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"bin",
|
|
||||||
"lib"
|
|
||||||
],
|
|
||||||
"keywords": [
|
|
||||||
"npm",
|
|
||||||
"download",
|
|
||||||
"count",
|
|
||||||
"monthly",
|
|
||||||
"daily",
|
|
||||||
"weekly",
|
|
||||||
"all",
|
|
||||||
"packages"
|
|
||||||
],
|
|
||||||
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
|
|
||||||
"license": "MPL-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@root/request": "^1.3.11",
|
|
||||||
"batchasync": "^1.0.2"
|
|
||||||
},
|
|
||||||
"bundledDependencies": [
|
|
||||||
"@root/request",
|
|
||||||
"batchasync"
|
|
||||||
]
|
|
||||||
}
|
|
16
test.js
16
test.js
@ -1,16 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var hnai = require('./');
|
|
||||||
hnai.packages('coolaj86').then(function(pkgnames) {
|
|
||||||
console.info('Total number of pakcages:', pkgnames.length);
|
|
||||||
hnai.downloads(pkgnames).then(function(downloads) {
|
|
||||||
console.log(
|
|
||||||
'Download Counts:',
|
|
||||||
downloads
|
|
||||||
.map(function(all) {
|
|
||||||
return all.package + ' ' + all.downloads;
|
|
||||||
})
|
|
||||||
.join('\n')
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
x
Reference in New Issue
Block a user