11cb0ef41Sopenharmony_ci// pass in an arborist object, and it'll output the data about what 21cb0ef41Sopenharmony_ci// was done, what was audited, etc. 31cb0ef41Sopenharmony_ci// 41cb0ef41Sopenharmony_ci// added ## packages, removed ## packages, and audited ## packages in 19.157s 51cb0ef41Sopenharmony_ci// 61cb0ef41Sopenharmony_ci// 1 package is looking for funding 71cb0ef41Sopenharmony_ci// run `npm fund` for details 81cb0ef41Sopenharmony_ci// 91cb0ef41Sopenharmony_ci// found 37 vulnerabilities (5 low, 7 moderate, 25 high) 101cb0ef41Sopenharmony_ci// run `npm audit fix` to fix them, or `npm audit` for details 111cb0ef41Sopenharmony_ci 121cb0ef41Sopenharmony_ciconst log = require('./log-shim.js') 131cb0ef41Sopenharmony_ciconst { depth } = require('treeverse') 141cb0ef41Sopenharmony_ciconst ms = require('ms') 151cb0ef41Sopenharmony_ciconst npmAuditReport = require('npm-audit-report') 161cb0ef41Sopenharmony_ciconst { readTree: getFundingInfo } = require('libnpmfund') 171cb0ef41Sopenharmony_ciconst auditError = require('./audit-error.js') 181cb0ef41Sopenharmony_ciconst Table = require('cli-table3') 191cb0ef41Sopenharmony_ci 201cb0ef41Sopenharmony_ci// TODO: output JSON if flatOptions.json is true 211cb0ef41Sopenharmony_ciconst reifyOutput = (npm, arb) => { 221cb0ef41Sopenharmony_ci const { diff, actualTree } = arb 231cb0ef41Sopenharmony_ci 241cb0ef41Sopenharmony_ci // note: fails and crashes if we're running audit fix and there was an error 251cb0ef41Sopenharmony_ci // which is a good thing, because there's no point printing all this other 261cb0ef41Sopenharmony_ci // stuff in that case! 271cb0ef41Sopenharmony_ci const auditReport = auditError(npm, arb.auditReport) ? null : arb.auditReport 281cb0ef41Sopenharmony_ci 291cb0ef41Sopenharmony_ci // don't print any info in --silent mode, but we still need to 301cb0ef41Sopenharmony_ci // set the exitCode properly from the audit report, if we have one. 311cb0ef41Sopenharmony_ci if (npm.silent) { 321cb0ef41Sopenharmony_ci getAuditReport(npm, auditReport) 331cb0ef41Sopenharmony_ci return 341cb0ef41Sopenharmony_ci } 351cb0ef41Sopenharmony_ci 361cb0ef41Sopenharmony_ci const summary = { 371cb0ef41Sopenharmony_ci added: 0, 381cb0ef41Sopenharmony_ci removed: 0, 391cb0ef41Sopenharmony_ci changed: 0, 401cb0ef41Sopenharmony_ci audited: auditReport && !auditReport.error ? actualTree.inventory.size : 0, 411cb0ef41Sopenharmony_ci funding: 0, 421cb0ef41Sopenharmony_ci } 431cb0ef41Sopenharmony_ci 441cb0ef41Sopenharmony_ci if (diff) { 451cb0ef41Sopenharmony_ci let diffTable 461cb0ef41Sopenharmony_ci if (npm.config.get('dry-run') || npm.config.get('long')) { 471cb0ef41Sopenharmony_ci diffTable = new Table({ 481cb0ef41Sopenharmony_ci chars: { 491cb0ef41Sopenharmony_ci top: '', 501cb0ef41Sopenharmony_ci 'top-mid': '', 511cb0ef41Sopenharmony_ci 'top-left': '', 521cb0ef41Sopenharmony_ci 'top-right': '', 531cb0ef41Sopenharmony_ci bottom: '', 541cb0ef41Sopenharmony_ci 'bottom-mid': '', 551cb0ef41Sopenharmony_ci 'bottom-left': '', 561cb0ef41Sopenharmony_ci 'bottom-right': '', 571cb0ef41Sopenharmony_ci left: '', 581cb0ef41Sopenharmony_ci 'left-mid': '', 591cb0ef41Sopenharmony_ci mid: '', 601cb0ef41Sopenharmony_ci 'mid-mid': '', 611cb0ef41Sopenharmony_ci right: '', 621cb0ef41Sopenharmony_ci 'right-mid': '', 631cb0ef41Sopenharmony_ci middle: ' ', 641cb0ef41Sopenharmony_ci }, 651cb0ef41Sopenharmony_ci style: { 661cb0ef41Sopenharmony_ci 'padding-left': 0, 671cb0ef41Sopenharmony_ci 'padding-right': 0, 681cb0ef41Sopenharmony_ci border: 0, 691cb0ef41Sopenharmony_ci }, 701cb0ef41Sopenharmony_ci }) 711cb0ef41Sopenharmony_ci } 721cb0ef41Sopenharmony_ci 731cb0ef41Sopenharmony_ci depth({ 741cb0ef41Sopenharmony_ci tree: diff, 751cb0ef41Sopenharmony_ci visit: d => { 761cb0ef41Sopenharmony_ci switch (d.action) { 771cb0ef41Sopenharmony_ci case 'REMOVE': 781cb0ef41Sopenharmony_ci diffTable?.push(['remove', d.actual.name, d.actual.package.version]) 791cb0ef41Sopenharmony_ci summary.removed++ 801cb0ef41Sopenharmony_ci break 811cb0ef41Sopenharmony_ci case 'ADD': 821cb0ef41Sopenharmony_ci diffTable?.push(['add', d.ideal.name, d.ideal.package.version]) 831cb0ef41Sopenharmony_ci actualTree.inventory.has(d.ideal) && summary.added++ 841cb0ef41Sopenharmony_ci break 851cb0ef41Sopenharmony_ci case 'CHANGE': 861cb0ef41Sopenharmony_ci diffTable?.push(['change', 871cb0ef41Sopenharmony_ci d.actual.name, 881cb0ef41Sopenharmony_ci d.actual.package.version + ' -> ' + d.ideal.package.version, 891cb0ef41Sopenharmony_ci ]) 901cb0ef41Sopenharmony_ci summary.changed++ 911cb0ef41Sopenharmony_ci break 921cb0ef41Sopenharmony_ci default: 931cb0ef41Sopenharmony_ci return 941cb0ef41Sopenharmony_ci } 951cb0ef41Sopenharmony_ci const node = d.actual || d.ideal 961cb0ef41Sopenharmony_ci log.silly(d.action, node.location) 971cb0ef41Sopenharmony_ci }, 981cb0ef41Sopenharmony_ci getChildren: d => d.children, 991cb0ef41Sopenharmony_ci }) 1001cb0ef41Sopenharmony_ci 1011cb0ef41Sopenharmony_ci if (diffTable) { 1021cb0ef41Sopenharmony_ci npm.output('\n' + diffTable.toString()) 1031cb0ef41Sopenharmony_ci } 1041cb0ef41Sopenharmony_ci } 1051cb0ef41Sopenharmony_ci 1061cb0ef41Sopenharmony_ci if (npm.flatOptions.fund) { 1071cb0ef41Sopenharmony_ci const fundingInfo = getFundingInfo(actualTree, { countOnly: true }) 1081cb0ef41Sopenharmony_ci summary.funding = fundingInfo.length 1091cb0ef41Sopenharmony_ci } 1101cb0ef41Sopenharmony_ci 1111cb0ef41Sopenharmony_ci if (npm.flatOptions.json) { 1121cb0ef41Sopenharmony_ci if (auditReport) { 1131cb0ef41Sopenharmony_ci // call this to set the exit code properly 1141cb0ef41Sopenharmony_ci getAuditReport(npm, auditReport) 1151cb0ef41Sopenharmony_ci summary.audit = npm.command === 'audit' ? auditReport 1161cb0ef41Sopenharmony_ci : auditReport.toJSON().metadata 1171cb0ef41Sopenharmony_ci } 1181cb0ef41Sopenharmony_ci npm.output(JSON.stringify(summary, null, 2)) 1191cb0ef41Sopenharmony_ci } else { 1201cb0ef41Sopenharmony_ci packagesChangedMessage(npm, summary) 1211cb0ef41Sopenharmony_ci packagesFundingMessage(npm, summary) 1221cb0ef41Sopenharmony_ci printAuditReport(npm, auditReport) 1231cb0ef41Sopenharmony_ci } 1241cb0ef41Sopenharmony_ci} 1251cb0ef41Sopenharmony_ci 1261cb0ef41Sopenharmony_ci// if we're running `npm audit fix`, then we print the full audit report 1271cb0ef41Sopenharmony_ci// at the end if there's still stuff, because it's silly for `npm audit` 1281cb0ef41Sopenharmony_ci// to tell you to run `npm audit` for details. otherwise, use the summary 1291cb0ef41Sopenharmony_ci// report. if we get here, we know it's not quiet or json. 1301cb0ef41Sopenharmony_ci// If the loglevel is silent, then we just run the report 1311cb0ef41Sopenharmony_ci// to get the exitCode set appropriately. 1321cb0ef41Sopenharmony_ciconst printAuditReport = (npm, report) => { 1331cb0ef41Sopenharmony_ci const res = getAuditReport(npm, report) 1341cb0ef41Sopenharmony_ci if (!res || !res.report) { 1351cb0ef41Sopenharmony_ci return 1361cb0ef41Sopenharmony_ci } 1371cb0ef41Sopenharmony_ci npm.output(`\n${res.report}`) 1381cb0ef41Sopenharmony_ci} 1391cb0ef41Sopenharmony_ci 1401cb0ef41Sopenharmony_ciconst getAuditReport = (npm, report) => { 1411cb0ef41Sopenharmony_ci if (!report) { 1421cb0ef41Sopenharmony_ci return 1431cb0ef41Sopenharmony_ci } 1441cb0ef41Sopenharmony_ci 1451cb0ef41Sopenharmony_ci // when in silent mode, we print nothing. the JSON output is 1461cb0ef41Sopenharmony_ci // going to just JSON.stringify() the report object. 1471cb0ef41Sopenharmony_ci const reporter = npm.silent ? 'quiet' 1481cb0ef41Sopenharmony_ci : npm.flatOptions.json ? 'quiet' 1491cb0ef41Sopenharmony_ci : npm.command !== 'audit' ? 'install' 1501cb0ef41Sopenharmony_ci : 'detail' 1511cb0ef41Sopenharmony_ci const defaultAuditLevel = npm.command !== 'audit' ? 'none' : 'low' 1521cb0ef41Sopenharmony_ci const auditLevel = npm.flatOptions.auditLevel || defaultAuditLevel 1531cb0ef41Sopenharmony_ci 1541cb0ef41Sopenharmony_ci const res = npmAuditReport(report, { 1551cb0ef41Sopenharmony_ci reporter, 1561cb0ef41Sopenharmony_ci ...npm.flatOptions, 1571cb0ef41Sopenharmony_ci auditLevel, 1581cb0ef41Sopenharmony_ci chalk: npm.chalk, 1591cb0ef41Sopenharmony_ci }) 1601cb0ef41Sopenharmony_ci if (npm.command === 'audit') { 1611cb0ef41Sopenharmony_ci process.exitCode = process.exitCode || res.exitCode 1621cb0ef41Sopenharmony_ci } 1631cb0ef41Sopenharmony_ci return res 1641cb0ef41Sopenharmony_ci} 1651cb0ef41Sopenharmony_ci 1661cb0ef41Sopenharmony_ciconst packagesChangedMessage = (npm, { added, removed, changed, audited }) => { 1671cb0ef41Sopenharmony_ci const msg = ['\n'] 1681cb0ef41Sopenharmony_ci if (added === 0 && removed === 0 && changed === 0) { 1691cb0ef41Sopenharmony_ci msg.push('up to date') 1701cb0ef41Sopenharmony_ci if (audited) { 1711cb0ef41Sopenharmony_ci msg.push(', ') 1721cb0ef41Sopenharmony_ci } 1731cb0ef41Sopenharmony_ci } else { 1741cb0ef41Sopenharmony_ci if (added) { 1751cb0ef41Sopenharmony_ci msg.push(`added ${added} package${added === 1 ? '' : 's'}`) 1761cb0ef41Sopenharmony_ci } 1771cb0ef41Sopenharmony_ci 1781cb0ef41Sopenharmony_ci if (removed) { 1791cb0ef41Sopenharmony_ci if (added) { 1801cb0ef41Sopenharmony_ci msg.push(', ') 1811cb0ef41Sopenharmony_ci } 1821cb0ef41Sopenharmony_ci 1831cb0ef41Sopenharmony_ci if (added && !audited && !changed) { 1841cb0ef41Sopenharmony_ci msg.push('and ') 1851cb0ef41Sopenharmony_ci } 1861cb0ef41Sopenharmony_ci 1871cb0ef41Sopenharmony_ci msg.push(`removed ${removed} package${removed === 1 ? '' : 's'}`) 1881cb0ef41Sopenharmony_ci } 1891cb0ef41Sopenharmony_ci if (changed) { 1901cb0ef41Sopenharmony_ci if (added || removed) { 1911cb0ef41Sopenharmony_ci msg.push(', ') 1921cb0ef41Sopenharmony_ci } 1931cb0ef41Sopenharmony_ci 1941cb0ef41Sopenharmony_ci if (!audited && (added || removed)) { 1951cb0ef41Sopenharmony_ci msg.push('and ') 1961cb0ef41Sopenharmony_ci } 1971cb0ef41Sopenharmony_ci 1981cb0ef41Sopenharmony_ci msg.push(`changed ${changed} package${changed === 1 ? '' : 's'}`) 1991cb0ef41Sopenharmony_ci } 2001cb0ef41Sopenharmony_ci if (audited) { 2011cb0ef41Sopenharmony_ci msg.push(', and ') 2021cb0ef41Sopenharmony_ci } 2031cb0ef41Sopenharmony_ci } 2041cb0ef41Sopenharmony_ci if (audited) { 2051cb0ef41Sopenharmony_ci msg.push(`audited ${audited} package${audited === 1 ? '' : 's'}`) 2061cb0ef41Sopenharmony_ci } 2071cb0ef41Sopenharmony_ci 2081cb0ef41Sopenharmony_ci msg.push(` in ${ms(Date.now() - npm.started)}`) 2091cb0ef41Sopenharmony_ci npm.output(msg.join('')) 2101cb0ef41Sopenharmony_ci} 2111cb0ef41Sopenharmony_ci 2121cb0ef41Sopenharmony_ciconst packagesFundingMessage = (npm, { funding }) => { 2131cb0ef41Sopenharmony_ci if (!funding) { 2141cb0ef41Sopenharmony_ci return 2151cb0ef41Sopenharmony_ci } 2161cb0ef41Sopenharmony_ci 2171cb0ef41Sopenharmony_ci npm.output('') 2181cb0ef41Sopenharmony_ci const pkg = funding === 1 ? 'package' : 'packages' 2191cb0ef41Sopenharmony_ci const is = funding === 1 ? 'is' : 'are' 2201cb0ef41Sopenharmony_ci npm.output(`${funding} ${pkg} ${is} looking for funding`) 2211cb0ef41Sopenharmony_ci npm.output(' run `npm fund` for details') 2221cb0ef41Sopenharmony_ci} 2231cb0ef41Sopenharmony_ci 2241cb0ef41Sopenharmony_cimodule.exports = reifyOutput 225