mirror of https://github.com/lukechilds/thanks.git
Feross Aboukhadijeh
7 years ago
commit
870d1e501a
4 changed files with 257 additions and 0 deletions
@ -0,0 +1,10 @@ |
|||||
|
# thanks |
||||
|
|
||||
|
### 🙌 Give thanks to the open source maintainers you depend on! ✨ |
||||
|
|
||||
|
## usage |
||||
|
|
||||
|
```js |
||||
|
npx thanks |
||||
|
``` |
||||
|
|
@ -0,0 +1,142 @@ |
|||||
|
#!/usr/bin/env node
|
||||
|
|
||||
|
const chalk = require('chalk') |
||||
|
const minimist = require('minimist') |
||||
|
const pify = require('pify') |
||||
|
const pkgDir = require('pkg-dir') |
||||
|
const readPackageTree = require('read-package-tree') |
||||
|
const RegistryClient = require('npm-registry-client') // TODO: use npm-registry-fetch
|
||||
|
const registryUrl = require('registry-url') |
||||
|
const stripAnsi = require('strip-ansi') |
||||
|
const textTable = require('text-table') |
||||
|
|
||||
|
const donees = require('./') |
||||
|
|
||||
|
const readPackageTreeAsync = pify(readPackageTree) |
||||
|
|
||||
|
init().catch(handleError) |
||||
|
|
||||
|
async function init () { |
||||
|
const argv = minimist(process.argv.slice(2)) |
||||
|
|
||||
|
const cwd = argv._[0] || process.cwd() |
||||
|
|
||||
|
const authors = {} |
||||
|
const client = createRegistryClient() |
||||
|
|
||||
|
// Get all packages in the nearest `node_modules` folder
|
||||
|
const rootPath = await pkgDir(cwd) |
||||
|
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
|
||||
|
// not include the list of maintainers
|
||||
|
const pkgs = await Promise.all(pkgNames.map(fetchPkg)) |
||||
|
|
||||
|
pkgs.forEach(pkg => { |
||||
|
pkg.maintainers |
||||
|
.map(maintainer => maintainer.name) |
||||
|
.forEach(author => addPackageAuthor(pkg.name, author)) |
||||
|
}) |
||||
|
|
||||
|
const rows = Object.keys(authors) |
||||
|
.filter(author => donees.authors[author] != null) |
||||
|
.sort((author1, author2) => authors[author2].length - authors[author1].length) |
||||
|
.map(author => { |
||||
|
const deps = authors[author] |
||||
|
return [ |
||||
|
chalk.green(author), |
||||
|
donees.authors[author], |
||||
|
`${deps.length} packages including ${deps.slice(0, 3).join(', ')}` |
||||
|
] |
||||
|
}) |
||||
|
|
||||
|
rows.unshift([ |
||||
|
chalk.underline('Author'), |
||||
|
chalk.underline('Where to Donate'), |
||||
|
chalk.underline('Dependencies') |
||||
|
]) |
||||
|
|
||||
|
const tableOpts = { |
||||
|
// align: ['l', 'l', 'l'],
|
||||
|
stringLength: str => stripAnsi(str).length |
||||
|
} |
||||
|
|
||||
|
const table = textTable(rows, tableOpts) |
||||
|
console.log(table) |
||||
|
|
||||
|
function createRegistryClient () { |
||||
|
const opts = { |
||||
|
log: { |
||||
|
error () {}, |
||||
|
http () {}, |
||||
|
info () {}, |
||||
|
silly () {}, |
||||
|
verbose () {}, |
||||
|
warn () {} |
||||
|
} |
||||
|
} |
||||
|
const client = new RegistryClient(opts) |
||||
|
client.getAsync = pify(client.get.bind(client)) |
||||
|
return client |
||||
|
} |
||||
|
|
||||
|
async function fetchPkg (pkgName) { |
||||
|
// The registry does not support fetching versions for scoped packages
|
||||
|
const isScopedPackage = pkgName.includes('/') |
||||
|
const url = isScopedPackage |
||||
|
? `${registryUrl()}${pkgName.replace('/', '%2F')}` |
||||
|
: `${registryUrl()}${pkgName}/latest` |
||||
|
|
||||
|
const opts = { |
||||
|
timeout: 30 * 1000, |
||||
|
staleOk: true |
||||
|
} |
||||
|
return client.getAsync(url, opts) |
||||
|
} |
||||
|
|
||||
|
function addPackageAuthor (pkgName, author) { |
||||
|
if (authors[author] == null) authors[author] = [] |
||||
|
authors[author].push(pkgName) |
||||
|
} |
||||
|
|
||||
|
// const rootPkg = await fetchLocalPkg()
|
||||
|
|
||||
|
// const rootDeps = [].concat(
|
||||
|
// findDeps(rootPkg, 'dependencies'),
|
||||
|
// findDeps(rootPkg, 'devDependencies'),
|
||||
|
// findDeps(rootPkg, 'optionalDependencies')
|
||||
|
// )
|
||||
|
|
||||
|
// const queue = [].push(...rootDeps)
|
||||
|
|
||||
|
// while (queue.length > 0) {
|
||||
|
// const pkgs = await Promise.all(queue.slice(0, CONCURRENCY).map(fetchPkg))
|
||||
|
// }
|
||||
|
} |
||||
|
|
||||
|
// async function fetchLocalPkg () {
|
||||
|
// const pkgPath = await pkgUp()
|
||||
|
// const pkgStr = await readFileAsync(pkgPath, 'utf8')
|
||||
|
|
||||
|
// try {
|
||||
|
// const pkg = JSON.parse(pkgStr)
|
||||
|
// normalizePackage(pkg)
|
||||
|
// return pkg
|
||||
|
// } catch (err) {
|
||||
|
// err.message = `Failed to parse package.json: ${err.message}`
|
||||
|
// throw err
|
||||
|
// }
|
||||
|
// }
|
||||
|
|
||||
|
// function findDeps (pkg, type) {
|
||||
|
// return pkg[type] && typeof pkg[type] === 'object'
|
||||
|
// ? Object.keys(pkg[type])
|
||||
|
// : []
|
||||
|
// }
|
||||
|
|
||||
|
function handleError (err) { |
||||
|
console.error(`thanks: Error: ${err.message}`) |
||||
|
console.error(err.stack) |
||||
|
process.exitCode = 1 |
||||
|
} |
@ -0,0 +1,54 @@ |
|||||
|
/* |
||||
|
* npm username -> donate page |
||||
|
* |
||||
|
* Whenever a `thanks` user has a package owned by one of these authors in their |
||||
|
* package tree, they will be prompted to donate. |
||||
|
*/ |
||||
|
const authors = { |
||||
|
andrewnez: 'https://en.liberapay.com/andrew/', |
||||
|
bevacqua: 'https://www.patreon.com/bevacqua', |
||||
|
feross: 'https://www.patreon.com/feross', |
||||
|
getify: 'https://www.patreon.com/getify', |
||||
|
hueniverse: 'https://www.patreon.com/eranhammer', |
||||
|
mafintosh: 'https://www.patreon.com/mafintosh', |
||||
|
mikeal: 'https://www.patreon.com/mikeal', |
||||
|
mmckegg: 'https://www.patreon.com/MattMcKegg', |
||||
|
mpj: 'https://www.patreon.com/funfunfunction', |
||||
|
noffle: 'https://en.liberapay.com/noffle/', |
||||
|
paulirish: 'https://en.liberapay.com/paulirish/', |
||||
|
sindresorhus: 'https://www.patreon.com/sindresorhus', |
||||
|
staltz: 'https://en.liberapay.com/andrestaltz/', |
||||
|
thlorenz: 'https://www.patreon.com/thlorenz', |
||||
|
yyx990803: 'https://www.patreon.com/evanyou' |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* npm package name -> donate page |
||||
|
* |
||||
|
* Whenever a `thanks` user has one these exact packages in their package tree, |
||||
|
* they will be prompted to donate. |
||||
|
*/ |
||||
|
const packages = { |
||||
|
'babel-core': 'https://opencollective.com/babel', |
||||
|
bower: 'https://opencollective.com/bower', |
||||
|
cheerio: 'https://opencollective.com/cheerio', |
||||
|
choo: 'https://opencollective.com/choo', |
||||
|
gulp: 'https://opencollective.com/gulpjs', |
||||
|
'gulp-cli': 'https://opencollective.com/gulpjs', |
||||
|
hoodie: 'https://opencollective.com/hoodie', |
||||
|
koa: 'https://opencollective.com/koajs', |
||||
|
'material-ui': 'https://opencollective.com/material-ui', |
||||
|
mocha: 'https://opencollective.com/mochajs', |
||||
|
parcel: 'https://opencollective.com/parcel', |
||||
|
preact: 'https://opencollective.com/preact', |
||||
|
pug: 'https://opencollective.com/pug', |
||||
|
'react-native-elements': 'https://opencollective.com/react-native-elements', |
||||
|
'redux-devtools-extension': 'https://opencollective.com/redux-devtools-extension', |
||||
|
rollup: 'https://opencollective.com/rollup', |
||||
|
'socket.io': 'https://opencollective.com/socketio', |
||||
|
tachyons: 'https://opencollective.com/tachyons', |
||||
|
webpack: 'https://opencollective.com/webpack', |
||||
|
yo: 'https://opencollective.com/yeoman' |
||||
|
} |
||||
|
|
||||
|
module.exports = { authors, packages } |
@ -0,0 +1,51 @@ |
|||||
|
{ |
||||
|
"name": "thanks", |
||||
|
"description": "Give thanks to the open source maintainers you depend on!", |
||||
|
"version": "0.0.0", |
||||
|
"author": { |
||||
|
"name": "Feross Aboukhadijeh", |
||||
|
"email": "feross@feross.org", |
||||
|
"url": "https://feross.org" |
||||
|
}, |
||||
|
"bin": "cmd.js", |
||||
|
"bugs": { |
||||
|
"url": "https://github.com/feross/thanks/issues" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"chalk": "^2.3.0", |
||||
|
"minimist": "^1.2.0", |
||||
|
"npm-registry-client": "^8.5.0", |
||||
|
"pify": "^3.0.0", |
||||
|
"pkg-dir": "^2.0.0", |
||||
|
"read-package-tree": "^5.1.6", |
||||
|
"registry-url": "^3.1.0", |
||||
|
"strip-ansi": "^4.0.0", |
||||
|
"text-table": "^0.2.0" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"standard": "*" |
||||
|
}, |
||||
|
"keywords": [ |
||||
|
"thanks", |
||||
|
"open source", |
||||
|
"sustainability", |
||||
|
"donate", |
||||
|
"support", |
||||
|
"funding", |
||||
|
"patreon", |
||||
|
"donor", |
||||
|
"thank you", |
||||
|
"fund", |
||||
|
"sustain", |
||||
|
"earn" |
||||
|
], |
||||
|
"license": "MIT", |
||||
|
"main": "index.js", |
||||
|
"repository": { |
||||
|
"type": "git", |
||||
|
"url": "git://github.com/feross/thanks.git" |
||||
|
}, |
||||
|
"scripts": { |
||||
|
"test": "standard" |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue