Browse Source

include list of **teams** seeking donations

master
Feross Aboukhadijeh 7 years ago
parent
commit
73ad7a74bd
  1. 99
      cmd.js

99
cmd.js

@ -68,36 +68,60 @@ async function init () {
spinner.text = chalk`Reading {cyan dependencies} from package tree in {magenta node_modules}...` spinner.text = chalk`Reading {cyan dependencies} from package tree in {magenta node_modules}...`
const rootPath = await pkgDir(cwd) const rootPath = await pkgDir(cwd)
const packageTree = await readPackageTreeAsync(rootPath) const packageTree = await readPackageTreeAsync(rootPath)
const pkgNames = packageTree.children.map(node => node.package.name)
// Get latest registry data on each local package, since the local data does // Get latest registry data on each local package, since the local data does
// not include the list of maintainers // not include the list of maintainers
spinner.text = chalk`Fetching package {cyan maintainers} from {red npm}...` spinner.text = chalk`Fetching package {cyan maintainers} from {red npm}...`
const pkgNames = packageTree.children.map(node => node.package.name)
const allPkgs = await Promise.all(pkgNames.map(pkgName => fetchPkg(client, pkgName))) const allPkgs = await Promise.all(pkgNames.map(pkgName => fetchPkg(client, pkgName)))
spinner.text = chalk`Fetching package {cyan download counts} from {red npm}...` spinner.text = chalk`Fetching package {cyan download counts} from {red npm}...`
const downloadCounts = await bulkFetchDownloads(pkgNames) const pkgDownloads = await bulkFetchPkgDownloads(pkgNames)
// Author name -> list of packages (sorted by direct dependencies, then download count) // Author name -> list of packages (sorted by direct dependencies, then download count)
const authorsPkgNames = computeAuthorsPkgNames(allPkgs, downloadCounts, directPkgNames) const authorsPkgNames = computeAuthorsPkgNames(allPkgs, pkgDownloads, directPkgNames)
// Array of author names who are seeking donations // Array of author names who are seeking donations (sorted by download count)
const authorsSeeking = Object.keys(authorsPkgNames) const authorsSeeking = Object.keys(authorsPkgNames)
.filter(author => thanks.authors[author] != null) .filter(author => thanks.authors[author] != null)
.sort((author1, author2) => authorsPkgNames[author2].length - authorsPkgNames[author1].length) .sort((author1, author2) => authorsPkgNames[author2].length - authorsPkgNames[author1].length)
const donateLinks = authorsSeeking // Array of package names that are seeking donations (sorted by download counte)
.map(author => thanks.authors[author]) const pkgNamesSeeking = pkgNames
.filter(pkgName => thanks.packages[pkgName] != null)
.sort((pkg1, pkg2) => pkgDownloads[pkg2] - pkgDownloads[pkg1])
const donateLinks = [].concat(
authorsSeeking.map(author => thanks.authors[author]),
pkgNamesSeeking.map(pkgName => thanks.packages[pkgName])
)
if (authorsSeeking.length) { const authorStr = chalk.cyan(`${authorsSeeking.length} authors`)
spinner.succeed(chalk`You depend on {cyan ${authorsSeeking.length} authors} who are {magenta seeking donations!} ✨\n`) const pkgNamesStr = chalk.cyan(`${pkgNamesSeeking.length} teams`)
printTable(authorsSeeking, authorsPkgNames, directPkgNames)
if (argv.open) openDonateLinks(donateLinks) if (authorsSeeking.length > 0 && pkgNamesSeeking.length > 0) {
spinner.succeed(
chalk`You depend on ${authorStr} and ${pkgNamesStr} who are {magenta seeking donations!} ✨\n`
)
} else if (authorsSeeking.length > 0) {
spinner.succeed(
chalk`You depend on ${authorStr} who are {magenta seeking donations!} ✨\n`
)
} else if (pkgNamesSeeking.length > 0) {
spinner.succeed(
chalk`You depend on ${pkgNamesStr} who are {magenta seeking donations!} ✨\n`
)
} else { } else {
spinner.info('You don\'t depend on any packages from maintainers seeking donations') spinner.succeed(
chalk`You depend on {cyan no authors} who are seeking donations! 😌`
)
} }
// TODO: compute list of **projects** seeking donations printTable(authorsSeeking, pkgNamesSeeking, authorsPkgNames, directPkgNames)
if (donateLinks.length && argv.open) {
openDonateLinks(donateLinks)
}
} }
function createRegistryClient () { function createRegistryClient () {
@ -134,30 +158,43 @@ async function fetchPkg (client, pkgName) {
return client.getAsync(url, opts) return client.getAsync(url, opts)
} }
function printTable (authorsSeeking, authorsPkgNames, directPkgNames) { function printTable (authorsSeeking, pkgNamesSeeking, authorsPkgNames, directPkgNames) {
const rows = authorsSeeking
.map(author => {
// Highlight direct dependencies in a different color // Highlight direct dependencies in a different color
const authorPkgNames = authorsPkgNames[author] function maybeHighlightPkgName (pkgName) {
.map(pkgName => {
return directPkgNames.includes(pkgName) return directPkgNames.includes(pkgName)
? chalk.green.bold(pkgName) ? chalk.green.bold(pkgName)
: pkgName : pkgName
}) }
const donateLink = thanks.authors[author].replace(RE_REMOVE_URL_PREFIX, '') const authorRows = authorsSeeking
.map(author => {
const authorPkgNames = authorsPkgNames[author].map(maybeHighlightPkgName)
const donateLink = prettyUrl(thanks.authors[author])
return [ return [
author, author,
chalk.cyan(donateLink), chalk.cyan(donateLink),
listWithMaxLen(authorPkgNames, termSize().columns - 45) listWithMaxLen(authorPkgNames, termSize().columns - 50)
]
})
const packageRows = pkgNamesSeeking
.map(pkgName => {
const donateLink = prettyUrl(thanks.packages[pkgName])
return [
`${pkgName} (team)`,
chalk.cyan(donateLink),
maybeHighlightPkgName(pkgName)
] ]
}) })
rows.unshift([ const rows = [[
chalk.underline('Author'), chalk.underline('Author'),
chalk.underline('Where to Donate'), chalk.underline('Where to Donate'),
chalk.underline('Dependencies') chalk.underline('Dependencies')
]) ]].concat(
authorRows,
packageRows
)
const opts = { const opts = {
stringLength: str => stripAnsi(str).length stringLength: str => stripAnsi(str).length
@ -166,11 +203,15 @@ function printTable (authorsSeeking, authorsPkgNames, directPkgNames) {
console.log(table + '\n') console.log(table + '\n')
} }
async function bulkFetchDownloads (pkgNames) { function prettyUrl (url) {
return url.replace(RE_REMOVE_URL_PREFIX, '')
}
async function bulkFetchPkgDownloads (pkgNames) {
// A few notes: // A few notes:
// - bulk queries do not support scoped packages // - bulk queries do not support scoped packages
// - bulk queries are limited to at most 128 packages at a time // - bulk queries are limited to at most 128 packages at a time
const downloads = {} const pkgDownloads = {}
const normalPkgNames = pkgNames.filter(pkgName => !isScopedPkg(pkgName)) const normalPkgNames = pkgNames.filter(pkgName => !isScopedPkg(pkgName))
const scopedPkgNames = pkgNames.filter(isScopedPkg) const scopedPkgNames = pkgNames.filter(isScopedPkg)
@ -180,20 +221,20 @@ async function bulkFetchDownloads (pkgNames) {
const url = DOWNLOADS_URL + pkgNamesSubset.join(',') const url = DOWNLOADS_URL + pkgNamesSubset.join(',')
const res = await got(url, { json: true }) const res = await got(url, { json: true })
Object.keys(res.body).forEach(pkgName => { Object.keys(res.body).forEach(pkgName => {
downloads[pkgName] = res.body[pkgName].downloads pkgDownloads[pkgName] = res.body[pkgName].downloads
}) })
} }
await Promise.all(scopedPkgNames.map(async scopedPkgName => { await Promise.all(scopedPkgNames.map(async scopedPkgName => {
const url = DOWNLOADS_URL + scopedPkgName const url = DOWNLOADS_URL + scopedPkgName
const res = await got(url, { json: true }) const res = await got(url, { json: true })
downloads[scopedPkgName] = res.body.downloads pkgDownloads[scopedPkgName] = res.body.downloads
})) }))
return downloads return pkgDownloads
} }
function computeAuthorsPkgNames (pkgs, downloadCounts, directPkgNames) { function computeAuthorsPkgNames (pkgs, pkgDownloads, directPkgNames) {
// author name -> array of package names // author name -> array of package names
const authorPkgNames = {} const authorPkgNames = {}
@ -214,7 +255,7 @@ function computeAuthorsPkgNames (pkgs, downloadCounts, directPkgNames) {
const pkgNames = authorPkgNames[author] const pkgNames = authorPkgNames[author]
.filter(pkgName => !authorDirectPkgNames.includes(pkgName)) .filter(pkgName => !authorDirectPkgNames.includes(pkgName))
.sort((pkg1, pkg2) => downloadCounts[pkg2] - downloadCounts[pkg1]) .sort((pkg1, pkg2) => pkgDownloads[pkg2] - pkgDownloads[pkg1])
pkgNames.unshift(...authorDirectPkgNames) pkgNames.unshift(...authorDirectPkgNames)

Loading…
Cancel
Save