diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..23f8942 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# folders +node_modules +.vscode + +# files +yarn.lock +package-lock.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..31ba22d --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "semi": false, + "singleQuote": true, + "printWidth": 120 +} diff --git a/README.md b/README.md index 85e6ff1..6fef557 100644 --- a/README.md +++ b/README.md @@ -1 +1,70 @@ -# WIP +
+

pkg-details

+ unicorn +

CLI tool to get pkg details

+ +![npm bundle size](https://img.shields.io/bundlephobia/minzip/pkg-details?label=size&style=flat-square) +![npm version](https://img.shields.io/npm/v/pkg-details?style=flat-square) +![npm downloads](https://img.shields.io/npm/dm/pkg-details?style=flat-square) +![gitHub tests](https://img.shields.io/github/workflow/status/sagar-gavhane/pkg-details/tests?label=tests&style=flat-square) + +
+ +### Installing + +This module is distributed via `npm` which is bundled with `node` and should be installed as one of your project's dependence. + +```bash +npm install -g pkg-details +# or +yarn global add pkg-details +``` + +### Usage + + + +```bash +npx pkg-details react +> +react@v16.13.1 | MIT | deps: 3 | versions: 275 | minified: 6.30 KB | gzip: 2.56 KB +React is a JavaScript library for building user interfaces. + +versions: +latest: 16.13.1 +next: 0.0.0-7f28234f8 +experimental: 0.0.0-experimental-4c8c98ab9 +canary: 0.0.0-57333ca33 +unstable: 0.0.0-da834083c + +downloads: +weekly: 7687396 +monthly: 29955078 + +links: +README: https://reactjs.org/ +npm: https://www.npmjs.com/package/react +repository: https://github.com/facebook/react.git + +dates: +last published: 4 months +created at: over 8 years + +maintainers: +- acdlite [npm@andrewclark.io] +- brianvaughn [briandavidvaughn@gmail.com] +- fb [opensource+npm@fb.com] +- gaearon [dan.abramov@gmail.com] +- lunaruan [lunaris.ruan@gmail.com] +- sebmarkbage [sebastian@calyptus.eu] +- sophiebits [npm@sophiebits.com] +- trueadm [dg@domgan.com] +``` + +### Contributing + +Pull requests are always welcome! :) + +### License + +MIT diff --git a/index.js b/index.js new file mode 100755 index 0000000..e4a993c --- /dev/null +++ b/index.js @@ -0,0 +1,98 @@ +#!/usr/bin/env node + +const chalk = require('chalk') +const argv = require('minimist')(process.argv.slice(2)) +const terminalLink = require('terminal-link') +const ora = require('ora') +const { subDays, format, parseISO, formatDistance } = require('date-fns') + +const { getPackage, getPackageStats, getDownloadStats } = require('./services/api') +const clear = require('./utils/clear') +const br = require('./utils/br') +const downloadSum = require('./utils/downloadSum') +const readablizeBytes = require('./utils/readablizeBytes') + +const spinner = ora('Loading package details...') + +// handle unhandled rejections +process.on('unhandledRejection', (err) => { + // clear() + spinner.clear() + console.log(chalk.red(`Failed while fetching package details :(`)) + process.exit() +}) + +const log = console.log + +const tl = (...args) => chalk.blueBright(terminalLink(...args)) + +if (!Array.isArray(argv._) || !argv._.length) { + log(chalk.bold.red('Please provide package name.')) + return +} + +const pkgName = argv._[0] + +const init = async () => { + // fetch package stats + spinner.start() + const [package, packageStats, weeklyDownloadStats, monthlyDownloadStats] = await Promise.all([ + await getPackage(pkgName), + await getPackageStats(pkgName), + await getDownloadStats(pkgName, format(subDays(new Date(), 7), 'yyyy-MM-dd'), format(new Date(), 'yyyy-MM-dd')), + await getDownloadStats(pkgName, format(subDays(new Date(), 28), 'yyyy-MM-dd'), format(new Date(), 'yyyy-MM-dd')), + ]) + spinner.stop() + + const npmhome = `https://www.npmjs.com/package/${pkgName}` + const homepage = package.homepage || npmhome + const tlConfig = { fallback: false } + const latestVersion = `${package['dist-tags'].latest}` + const numberOfDeps = Object.keys(package.versions[latestVersion].dependencies).length + const numberOfVersions = Object.keys(package.versions).length + const repositoryUrl = package.repository.url.replace('git+', '') + const lastPublished = package.time[latestVersion] + + // printout + clear() + br() + log( + chalk.green.underline.bold(`${pkgName}@v${latestVersion}`), + `| ${package.license} | deps: ${chalk.cyan(numberOfDeps)} | versions: ${chalk.cyan(numberOfVersions)}`, + `| minified: ${chalk.cyan(readablizeBytes(packageStats.size))} | gzip: ${chalk.cyan( + readablizeBytes(packageStats.gzip) + )}` + ) + console.log(`${package.description}`) + br() + + console.log(chalk.yellow.bold('versions:')) + for (const key in package['dist-tags']) { + console.log(`${key}: ${chalk.cyan(package['dist-tags'][key])}`) + } + br() + + console.log(chalk.yellow.bold('downloads:')) + console.log(`weekly: ${chalk.cyan(downloadSum(weeklyDownloadStats.downloads))}`) + console.log(`monthly: ${chalk.cyan(downloadSum(monthlyDownloadStats.downloads))}`) + + br() + console.log(chalk.yellow.bold('links:')) + console.log(`README: ${tl(homepage, homepage, tlConfig)}`) + console.log(`npm: ${tl(npmhome, npmhome, tlConfig)}`) + console.log(`repository: ${tl(repositoryUrl, repositoryUrl, tlConfig)}`) + + br() + console.log(chalk.yellow.bold('dates:')) + // 2020-03-19T19:53:13.309Z + console.log(`last published: ${formatDistance(parseISO(lastPublished), new Date())}`) + console.log(`created at: ${formatDistance(parseISO(package.time.created), new Date())}`) + + br() + console.log(chalk.yellow.bold('maintainers:')) + package.maintainers.forEach((maintainer) => { + console.log(`- ${maintainer.name} [${tl(maintainer.email, `mailto:${maintainer.email}`)}]`) + }) +} + +init() diff --git a/package.json b/package.json index b40e573..f151db3 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,31 @@ "version": "1.0.0", "description": "get npm package details", "main": "index.js", + "bin": { + "pkg-details": "index.js" + }, "repository": "git@github.com:sagar-gavhane/pkg-details.git", "author": "sagar", "license": "MIT", - "private": false + "private": false, + "keywords": [ + "pkg details", + "details", + "pkg-info", + "npm-info" + ], + "scripts": { + "release": "np" + }, + "devDependencies": { + "np": "^6.3.2" + }, + "dependencies": { + "chalk": "^4.1.0", + "date-fns": "^2.15.0", + "got": "^11.5.1", + "minimist": "^1.2.5", + "ora": "^4.0.5", + "terminal-link": "^2.1.1" + } } diff --git a/services/api.js b/services/api.js new file mode 100644 index 0000000..c85bdb0 --- /dev/null +++ b/services/api.js @@ -0,0 +1,25 @@ +const got = require('got') + +const getPackage = async (packageName) => { + return await got(`https://registry.npmjs.org/${packageName}`, { + responseType: 'json', + }).then((r) => r.body) +} + +const getPackageStats = async (packageName) => { + return await got(`https://bundlephobia.com/api/size?package=${packageName}`, { + responseType: 'json', + }).then((r) => r.body) +} + +const getDownloadStats = async (packageName, startDate, endDate) => { + return await got(`https://api.npmjs.org/downloads/range/${startDate}:${endDate}/${packageName}`, { + responseType: 'json', + }).then((r) => r.body) +} + +module.exports = { + getPackage, + getPackageStats, + getDownloadStats, +} diff --git a/utils/br.js b/utils/br.js new file mode 100644 index 0000000..fc386ab --- /dev/null +++ b/utils/br.js @@ -0,0 +1,5 @@ +const clear = () => { + console.log('') +} + +module.exports = clear diff --git a/utils/clear.js b/utils/clear.js new file mode 100644 index 0000000..02ab514 --- /dev/null +++ b/utils/clear.js @@ -0,0 +1,9 @@ +const clear = () => { + const readline = require('readline') + const blank = '\n'.repeat(process.stdout.rows) + console.log(blank) + readline.cursorTo(process.stdout, 0, 0) + readline.clearScreenDown(process.stdout) +} + +module.exports = clear diff --git a/utils/downloadSum.js b/utils/downloadSum.js new file mode 100644 index 0000000..0e97b42 --- /dev/null +++ b/utils/downloadSum.js @@ -0,0 +1,5 @@ +const downloadSum = (downloads) => { + return downloads.reduce((prev, currentValue) => prev + currentValue.downloads, 0) +} + +module.exports = downloadSum diff --git a/utils/readablizeBytes.js b/utils/readablizeBytes.js new file mode 100644 index 0000000..1f8115d --- /dev/null +++ b/utils/readablizeBytes.js @@ -0,0 +1,7 @@ +const readablizeBytes = (bytes) => { + var s = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'] + var e = Math.floor(Math.log(bytes) / Math.log(1024)) + return (bytes / Math.pow(1024, e)).toFixed(2) + ' ' + s[e] +} + +module.exports = readablizeBytes