const archy = require('archy')
const Arborist = require('@npmcli/arborist')
const chalk = require('chalk')
const pacote = require('pacote')
const semver = require('semver')
const npa = require('npm-package-arg')
const { depth } = require('treeverse')
const { readTree: getFundingInfo, normalizeFunding, isValidFunding } = require('libnpmfund')
const completion = require('../utils/completion/installed-deep.js')
const openUrl = require('../utils/open-url.js')
const ArboristWorkspaceCmd = require('../arborist-cmd.js')
const getPrintableName = ({ name, version }) => {
const printableVersion = version ? `@${version}` : ''
return `${name}${printableVersion}`
}
class Fund extends ArboristWorkspaceCmd {
static description = 'Retrieve funding information'
static name = 'fund'
static params = ['json', 'browser', 'unicode', 'workspace', 'which']
static usage = ['[<package-spec>]']
// TODO
/* istanbul ignore next */
async completion (opts) {
return completion(this.npm, opts)
}
async exec (args) {
const spec = args[0]
const numberArg = this.npm.config.get('which')
const fundingSourceNumber = numberArg && parseInt(numberArg, 10)
const badFundingSourceNumber =
numberArg !== null && (String(fundingSourceNumber) !== numberArg || fundingSourceNumber < 1)
if (badFundingSourceNumber) {
const err = new Error(
'`npm fund [<@scope>/]<pkg> [--which=fundingSourceNumber]` must be given a positive integer'
)
err.code = 'EFUNDNUMBER'
throw err
}
if (this.npm.global) {
const err = new Error('`npm fund` does not support global packages')
err.code = 'EFUNDGLOBAL'
throw err
}
const where = this.npm.prefix
const arb = new Arborist({ ...this.npm.flatOptions, path: where })
const tree = await arb.loadActual()
if (spec) {
await this.openFundingUrl({
path: where,
tree,
spec,
fundingSourceNumber,
})
return
}
// TODO: add !workspacesEnabled option handling to libnpmfund
const fundingInfo = getFundingInfo(tree, {
...this.flatOptions,
workspaces: this.workspaceNames,
})
if (this.npm.config.get('json')) {
this.npm.output(this.printJSON(fundingInfo))
} else {
this.npm.output(this.printHuman(fundingInfo))
}
}
printJSON (fundingInfo) {
return JSON.stringify(fundingInfo, null, 2)
}
printHuman (fundingInfo) {
const color = this.npm.color
const unicode = this.npm.config.get('unicode')
const seenUrls = new Map()
const tree = obj => archy(obj, '', { unicode })
const result = depth({
tree: fundingInfo,
// composes human readable package name
// and creates a new archy item for readable output
visit: ({ name, version, funding }) => {
const [fundingSource] = [].concat(normalizeFunding(funding)).filter(isValidFunding)
const { url } = fundingSource || {}
const pkgRef = getPrintableName({ name, version })
let item = {
label: pkgRef,
}
if (url) {
item.label = tree({
label: color ? chalk.bgBlack.white(url) : url,
nodes: [pkgRef],
}).trim()
// stacks all packages together under the same item
if (seenUrls.has(url)) {
item = seenUrls.get(url)
item.label += `, ${pkgRef}`
return null
} else {
seenUrls.set(url, item)
}
}
return item
},
// puts child nodes back into returned archy
// output while also filtering out missing items
leave: (item, children) => {
if (item) {
item.nodes = children.filter(Boolean)
}
return item
},
// turns tree-like object return by libnpmfund
// into children to be properly read by treeverse
getChildren: node =>
Object.keys(node.dependencies || {}).map(key => ({
name: key,
...node.dependencies[key],
})),
})
const res = tree(result)
return color ? chalk.reset(res) : res
}
async openFundingUrl ({ path, tree, spec, fundingSourceNumber }) {
const arg = npa(spec, path)
const retrievePackageMetadata = () => {
if (arg.type === 'directory') {
if (tree.path === arg.fetchSpec) {
// matches cwd, e.g: npm fund .
return tree.package
} else {
// matches any file path within current arborist inventory
for (const item of tree.inventory.values()) {
if (item.path === arg.fetchSpec) {
return item.package
}
}
}
} else {
// tries to retrieve a package from arborist inventory
// by matching resulted package name from the provided spec
const [item] = [...tree.inventory.query('name', arg.name)]
.filter(i => semver.valid(i.package.version))
.sort((a, b) => semver.rcompare(a.package.version, b.package.version))
if (item) {
return item.package
}
}
}
const { funding } =
retrievePackageMetadata() ||
(await pacote.manifest(arg, this.npm.flatOptions).catch(() => ({})))
const validSources = [].concat(normalizeFunding(funding)).filter(isValidFunding)
const matchesValidSource =
validSources.length === 1 ||
(fundingSourceNumber > 0 && fundingSourceNumber <= validSources.length)
if (matchesValidSource) {
const index = fundingSourceNumber ? fundingSourceNumber - 1 : 0
const { type, url } = validSources[index]
const typePrefix = type ? `${type} funding` : 'Funding'
const msg = `${typePrefix} available at the following URL`
return openUrl(this.npm, url, msg)
} else if (validSources.length && !(fundingSourceNumber >= 1)) {
validSources.forEach(({ type, url }, i) => {
const typePrefix = type ? `${type} funding` : 'Funding'
const msg = `${typePrefix} available at the following URL`
this.npm.output(`${i + 1}: ${msg}: ${url}`)
})
this.npm.output(
/* eslint-disable-next-line max-len */
'Run `npm fund [<@scope>/]<pkg> --which=1`, for example, to open the first funding URL listed in that package'
)
} else {
const noFundingError = new Error(`No valid funding method available for: ${spec}`)
noFundingError.code = 'ENOFUND'
throw noFundingError
}
}
}
module.exports = Fund
| Name | Type | Size | Permission | Actions |
|---|---|---|---|---|
| access.js | File | 5.45 KB | 0644 |
|
| adduser.js | File | 2.2 KB | 0644 |
|
| audit.js | File | 11.95 KB | 0644 |
|
| bin.js | File | 729 B | 0644 |
|
| birthday.js | File | 508 B | 0644 |
|
| bugs.js | File | 815 B | 0644 |
|
| cache.js | File | 7.08 KB | 0644 |
|
| ci.js | File | 3.63 KB | 0644 |
|
| completion.js | File | 8.91 KB | 0644 |
|
| config.js | File | 8.11 KB | 0644 |
|
| dedupe.js | File | 1.37 KB | 0644 |
|
| deprecate.js | File | 2.06 KB | 0644 |
|
| diff.js | File | 8.1 KB | 0644 |
|
| dist-tag.js | File | 5.47 KB | 0644 |
|
| docs.js | File | 447 B | 0644 |
|
| doctor.js | File | 9.22 KB | 0644 |
|
| edit.js | File | 2 KB | 0644 |
|
| exec.js | File | 2.44 KB | 0644 |
|
| explain.js | File | 3.55 KB | 0644 |
|
| explore.js | File | 2.33 KB | 0644 |
|
| find-dupes.js | File | 602 B | 0644 |
|
| fund.js | File | 6.37 KB | 0644 |
|
| get.js | File | 524 B | 0644 |
|
| help-search.js | File | 5.62 KB | 0644 |
|
| help.js | File | 4.53 KB | 0644 |
|
| hook.js | File | 3.93 KB | 0644 |
|
| init.js | File | 6.81 KB | 0644 |
|
| install-ci-test.js | File | 377 B | 0644 |
|
| install-test.js | File | 374 B | 0644 |
|
| install.js | File | 5.11 KB | 0644 |
|
| link.js | File | 5.02 KB | 0644 |
|
| ll.js | File | 234 B | 0644 |
|
| logout.js | File | 1.34 KB | 0644 |
|
| ls.js | File | 16.94 KB | 0644 |
|
| org.js | File | 4.2 KB | 0644 |
|
| outdated.js | File | 8.84 KB | 0644 |
|
| owner.js | File | 5.88 KB | 0644 |
|
| pack.js | File | 2.36 KB | 0644 |
|
| ping.js | File | 874 B | 0644 |
|
| pkg.js | File | 3.47 KB | 0644 |
|
| prefix.js | File | 343 B | 0644 |
|
| profile.js | File | 11.25 KB | 0644 |
|
| prune.js | File | 779 B | 0644 |
|
| publish.js | File | 6.33 KB | 0644 |
|
| query.js | File | 2.81 KB | 0644 |
|
| rebuild.js | File | 2.16 KB | 0644 |
|
| repo.js | File | 1.24 KB | 0644 |
|
| restart.js | File | 351 B | 0644 |
|
| root.js | File | 298 B | 0644 |
|
| run-script.js | File | 6.9 KB | 0644 |
|
| search.js | File | 2.72 KB | 0644 |
|
| set-script.js | File | 2.63 KB | 0644 |
|
| set.js | File | 572 B | 0644 |
|
| shrinkwrap.js | File | 2.64 KB | 0644 |
|
| star.js | File | 1.87 KB | 0644 |
|
| stars.js | File | 1.03 KB | 0644 |
|
| start.js | File | 341 B | 0644 |
|
| stop.js | File | 336 B | 0644 |
|
| team.js | File | 4.44 KB | 0644 |
|
| test.js | File | 336 B | 0644 |
|
| token.js | File | 6.79 KB | 0644 |
|
| uninstall.js | File | 1.52 KB | 0644 |
|
| unpublish.js | File | 4.51 KB | 0644 |
|
| unstar.js | File | 182 B | 0644 |
|
| update.js | File | 1.7 KB | 0644 |
|
| version.js | File | 3.6 KB | 0644 |
|
| view.js | File | 14.38 KB | 0644 |
|
| whoami.js | File | 514 B | 0644 |
|