Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

npm-pkg-stats

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

npm-pkg-stats - npm Package Compare versions

Comparing version 1.4.0 to 1.5.0

__snapshots__/api.test.js.snap

205

api.js

@@ -1,18 +0,18 @@

const dayjs = require('dayjs');
const getRepoUrl = require('get-repository-url');
const parseRepoUrl = require('parse-github-url');
const parseGithubUrl = require('parse-github-url');
const R = require('ramda');
const Table = require('cli-table');
const { GraphQLClient } = require('graphql-request');
const {
calcRatio,
formatNumber,
formatPercentage,
formatSize,
} = require('./util');
const util = require('./util');
const nilValue = '--';
const api = {};
const makeQuery = (package, owner) => `{
repository(name: "${package}", owner: "${owner}"){
api.nilValue = '--';
api.getRepoUrl = async pkg => await getRepoUrl(pkg);
api.makeOwnerAndPkgNameBy = R.pipe(parseGithubUrl, R.pick(['owner', 'name']));
api.makeQuery = (pkg, owner) => `{
repository(name: "${pkg}", owner: "${owner}"){
openIssues: issues(filterBy: { states: [OPEN]}) {

@@ -52,20 +52,14 @@ totalCount

function makeVerticalTable ({
githubStats,
npmStats,
package,
}) {
const stats = {
...npmStats,
...githubStats,
};
const tableStyle = {
head: ['magenta'],
};
api.makeVerticalTable = function makeVerticalTable({ pkg, ...stats }) {
const table = new Table({
style: {
head: ['magenta'],
},
head: ['package', package],
style: tableStyle,
head: ['package', pkg],
});
const rows = R.pipe(
R.keys,
R.map(key => ({ [key]: stats[key] }))
R.map(key => ({ [key]: stats[key] })),
)(stats);

@@ -75,18 +69,29 @@ table.push(...rows);

return table;
}
};
function makeNpmStats ({ bundlephobiaData, npmDownloadData }) {
const weeklyNpmDownloads = R.pipe(
R.prop('downloads'),
formatNumber
)(npmDownloadData);
api.makeHorizontalTable = function makeHorizontalTable({
labelList,
valueList,
}) {
const table = new Table({
style: tableStyle,
head: labelList,
});
table.push(...valueList);
return table;
};
api.makeNpmStats = function makeNpmStats({
bundlephobiaData,
npmDownloadData,
}) {
return {
version: R.pipe(
R.prop('version'),
R.defaultTo(nilValue)
R.defaultTo(api.nilValue),
)(bundlephobiaData),
dependencies: R.pipe(
R.prop('dependencyCount'),
formatNumber
util.formatNumber,
)(bundlephobiaData),

@@ -97,18 +102,30 @@ 'gzip size': R.pipe(

R.isNil,
R.always(nilValue),
R.always(api.nilValue),
R.pipe(
formatSize,
({ size, unit }) => `${parseFloat(size).toFixed(1)} ${unit}`
)
)
util.formatSize,
({ size, unit }) => `${parseFloat(size).toFixed(1)} ${unit}`,
),
),
)(bundlephobiaData),
'weekly npm downloads': weeklyNpmDownloads,
'weekly npm downloads': R.pipe(
R.prop('downloads'),
util.formatNumber,
)(npmDownloadData),
};
}
};
function makeGithubStats ({ githubData }) {
const openIssues = R.path(['repository', 'openIssues', 'totalCount'], githubData);
const closedIssues = R.path(['repository', 'closedIssues', 'totalCount'], githubData);
api.makeGithubStats = function makeGithubStats({ githubData = {} }) {
const openIssues = R.path(
['repository', 'openIssues', 'totalCount'],
githubData,
);
const closedIssues = R.path(
['repository', 'closedIssues', 'totalCount'],
githubData,
);
const openPRs = R.path(['repository', 'openPRs', 'totalCount'], githubData);
const closedPRs = R.path(['repository', 'closedPRs', 'totalCount'], githubData);
const closedPRs = R.path(
['repository', 'closedPRs', 'totalCount'],
githubData,
);

@@ -118,60 +135,64 @@ return {

R.path(['repository', 'stargazers', 'totalCount']),
formatNumber
util.formatNumber,
)(githubData),
'open PRs': formatNumber(openPRs),
'open PRs (% of total)': formatPercentage(calcRatio(openPRs, closedPRs)),
'closed PRs': formatNumber(closedPRs),
'open issues': formatNumber(openIssues),
'open issues (% of total)': formatPercentage(calcRatio(openIssues, closedIssues)),
'closed issues': formatNumber(closedIssues),
'open PRs': util.formatNumber(openPRs),
'open PRs (% of total)': util.formatPercentage(
util.calcRatio(openPRs, closedPRs),
),
'closed PRs': util.formatNumber(closedPRs),
'open issues': util.formatNumber(openIssues),
'open issues (% of total)': util.formatPercentage(
util.calcRatio(openIssues, closedIssues),
),
'closed issues': util.formatNumber(closedIssues),
'last release': R.pipe(
R.path(['repository', 'releases', 'nodes']),
R.defaultTo([]),
R.head,
R.prop('publishedAt'),
date => dayjs(date).format('YYYY-MM-DD')
util.formatDate,
)(githubData),
license: R.pipe(
R.path(['repository', 'licenseInfo', 'name']),
R.defaultTo(nilValue)
R.defaultTo(api.nilValue),
)(githubData),
};
}
};
async function getStats (package, token) {
api.getStats = R.curry(async function getStats(token, pkg) {
if (R.isNil(token)) {
console.error('No NPM_PKG_STATS_TOKEN found in your environment variables. Please follow the installation instructions.');
return;
throw new Error(
'No NPM_PKG_STATS_TOKEN found in your environment variables. Please follow the installation instructions: https://github.com/jacobworrel/npm-pkg-stats#installation--usage.',
);
}
const [ bundlephobiaData, npmDownloadData ] = await Promise.all([
fetchBundlephobiaData(package),
fetchNpmDownload(package),
const [bundlephobiaData, npmDownloadData] = await Promise.all([
api.fetchBundlephobiaData(pkg),
api.fetchNpmDownload(pkg),
]);
const npmStats = api.makeNpmStats({ bundlephobiaData, npmDownloadData });
const npmStats = makeNpmStats({ bundlephobiaData, npmDownloadData });
const repoUrl = await getRepoUrl(package);
const repoUrl = await api.getRepoUrl(pkg);
if (R.isNil(repoUrl)) {
console.warn(`Requested package has no repository url in package.json so we were unable to gather stats from GitHub.`);
console.log(makeVerticalTable({ npmStats, package }).toString());
return;
console.warn(
`Requested package "${pkg}" has no repository url in package.json so we were unable to gather stats from GitHub.`,
);
return {
pkg,
...npmStats,
};
}
const {
owner,
name: githubPackageName,
} = R.pipe(
parseRepoUrl,
R.pick(['owner', 'name'])
)(repoUrl);
const { owner, name: githubPackageName } = api.makeOwnerAndPkgNameBy(repoUrl);
const githubData = await api.fetchGithubData(githubPackageName, owner, token);
const githubStats = api.makeGithubStats({ githubData });
const githubData = await fetchGithubData(githubPackageName, owner, token);
return {
pkg,
...npmStats,
...githubStats,
};
});
const githubStats = makeGithubStats({ githubData });
console.log('\n');
console.log(makeVerticalTable({ npmStats, githubStats, package }).toString());
}
async function fetchGithubData (package, owner, token) {
api.fetchGithubData = async function fetchGithubData(pkg, owner, token) {
const graphQLClient = new GraphQLClient('https://api.github.com/graphql', {

@@ -182,17 +203,17 @@ headers: {

});
return await graphQLClient.request(makeQuery(package, owner));
}
return await graphQLClient.request(api.makeQuery(pkg, owner));
};
async function fetchBundlephobiaData (package) {
const resp = await fetch(`https://bundlephobia.com/api/size?package=${package}`);
api.fetchBundlephobiaData = async function fetchBundlephobiaData(pkg) {
const resp = await fetch(`https://bundlephobia.com/api/size?package=${pkg}`);
return await resp.json();
}
};
async function fetchNpmDownload (package) {
const resp = await fetch(`https://api.npmjs.org/downloads/point/last-week/${package}`);
api.fetchNpmDownload = async function fetchNpmDownload(pkg) {
const resp = await fetch(
`https://api.npmjs.org/downloads/point/last-week/${pkg}`,
);
return await resp.json();
}
};
module.exports = {
getStats,
};
module.exports = api;
#!/usr/bin/env node
const api = require('./api');
const chalk = require('chalk');

@@ -7,12 +8,11 @@ const figlet = require('figlet');

const R = require('ramda');
const util = require('./util');
const { getStats } = require('./api');
/**
* TODO
* - add support for multiple packages (ie. comparison mode)
*/
const [ package ] = R.drop(2, process.argv);
const pkgList = R.drop(2, process.argv);
const token = process.env.NPM_PKG_STATS_TOKEN;

@@ -29,4 +29,32 @@

getStats(package, token)
.then(() => spinner.stop())
.catch(err => console.error(err));
Promise.all(R.map(getStats(token), pkgList))
.then(
R.pipe(
R.tap(() => console.log('\n')),
R.ifElse(
R.pipe(R.length, R.equals(1)),
R.pipe(R.head, api.makeVerticalTable, R.toString, console.log),
pkgStatsList => {
const labelList = R.pipe(
R.reduce(
(result, pkgStats) => R.concat(R.keys(pkgStats), result),
[],
),
R.uniq,
)(pkgStatsList);
const valueList = R.map(
R.pipe(R.values, util.fillWith(api.nilValue, labelList.length)),
pkgStatsList,
);
console.log(
api.makeHorizontalTable({ labelList, valueList }).toString(),
);
},
),
() => spinner.stop(),
),
)
.catch(err => {
console.error(err);
spinner.stop();
});
{
"name": "npm-pkg-stats",
"bin": "./index.js",
"version": "1.4.0",
"version": "1.5.0",
"description": "A CLI tool that gets stats on NPM packages.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "eslint . --ext .js",
"test": "jest",
"test:watch": "jest --watchAll",
"test:cover": "jest --coverage",
"start": "node index.js"

@@ -27,3 +30,14 @@ },

"ramda": "^0.26.1"
},
"devDependencies": {
"eslint": "^6.6.0",
"eslint-config-prettier": "^6.5.0",
"eslint-plugin-prettier": "^3.1.1",
"jest": "^24.9.0",
"prettier": "1.19.1"
},
"prettier": {
"trailingComma": "all",
"singleQuote": true
}
}

@@ -60,2 +60,20 @@ # npm-pkg-stats

### Multiple Packages
It also works if you want to compare multiple packages side by side.
For example, `npm-pkg-stats ramda lodash underscore` prints the following to the console:
```
┌────────────┬─────────┬──────────────┬───────────┬──────────────────────┬──────────────┬──────────┬───────────────────────┬────────────┬─────────────┬──────────────────────────┬───────────────┬──────────────┬─────────────┐
│ pkg │ version │ dependencies │ gzip size │ weekly npm downloads │ github stars │ open PRs │ open PRs (% of total) │ closed PRs │ open issues │ open issues (% of total) │ closed issues │ last release │ license │
├────────────┼─────────┼──────────────┼───────────┼──────────────────────┼──────────────┼──────────┼───────────────────────┼────────────┼─────────────┼──────────────────────────┼───────────────┼──────────────┼─────────────┤
│ ramda │ 0.26.1 │ 0 │ 12.3 kB │ 5,611,712 │ 17,525 │ 97 │ 24.31% │ 302 │ 172 │ 12.49% │ 1,205 │ 2019-05-26 │ MIT License │
├────────────┼─────────┼──────────────┼───────────┼──────────────────────┼──────────────┼──────────┼───────────────────────┼────────────┼─────────────┼──────────────────────────┼───────────────┼──────────────┼─────────────┤
│ lodash │ 4.17.15 │ 0 │ 24.3 kB │ 25,955,925 │ 42,361 │ 11 │ 2.39% │ 449 │ 8 │ 0.23% │ 3,516 │ 2016-01-12 │ Other │
├────────────┼─────────┼──────────────┼───────────┼──────────────────────┼──────────────┼──────────┼───────────────────────┼────────────┼─────────────┼──────────────────────────┼───────────────┼──────────────┼─────────────┤
│ underscore │ 1.9.1 │ 0 │ 6.3 kB │ 6,645,455 │ 24,984 │ 53 │ 6.83% │ 723 │ 70 │ 5.22% │ 1,272 │ 2019-11-17 │ MIT License │
└────────────┴─────────┴──────────────┴───────────┴──────────────────────┴──────────────┴──────────┴───────────────────────┴────────────┴─────────────┴──────────────────────────┴───────────────┴──────────────┴─────────────┘
```
## Stats

@@ -62,0 +80,0 @@

@@ -0,5 +1,6 @@

const dayjs = require('dayjs');
const numeral = require('numeral');
const R = require('ramda');
function calcRatio (open, closed) {
function calcRatio(open, closed) {
const total = R.add(open, closed);

@@ -9,7 +10,11 @@ return open / total;

function formatNumber (x) {
function formatDate(date) {
return dayjs(date).format('YYYY-MM-DD');
}
function formatNumber(x) {
return numeral(x).format('0,0');
}
function formatPercentage (x) {
function formatPercentage(x) {
return numeral(x).format('0.00%');

@@ -19,23 +24,29 @@ }

// copied from bundlephobia source
function formatSize (value) {
function formatSize(value) {
let unit, size;
if (Math.log10(value) < 3) {
unit = 'B';
size = value
size = value;
} else if (Math.log10(value) < 6) {
unit = 'kB';
size = value / 1024
size = value / 1024;
} else {
unit = 'mB';
size = value / 1024 / 1024
size = value / 1024 / 1024;
}
return { unit, size }
return { unit, size };
}
const fillWith = R.curry((defaultValue, length, list) =>
R.times(idx => R.defaultTo(defaultValue, list[idx]), length),
);
module.exports = {
calcRatio,
fillWith,
formatDate,
formatNumber,
formatPercentage,
calcRatio,
formatSize,
};
};
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc