11cb0ef41Sopenharmony_ciconst cacache = require('cacache') 21cb0ef41Sopenharmony_ciconst fs = require('fs') 31cb0ef41Sopenharmony_ciconst fetch = require('make-fetch-happen') 41cb0ef41Sopenharmony_ciconst Table = require('cli-table3') 51cb0ef41Sopenharmony_ciconst which = require('which') 61cb0ef41Sopenharmony_ciconst pacote = require('pacote') 71cb0ef41Sopenharmony_ciconst { resolve } = require('path') 81cb0ef41Sopenharmony_ciconst semver = require('semver') 91cb0ef41Sopenharmony_ciconst { promisify } = require('util') 101cb0ef41Sopenharmony_ciconst log = require('../utils/log-shim.js') 111cb0ef41Sopenharmony_ciconst ping = require('../utils/ping.js') 121cb0ef41Sopenharmony_ciconst { defaults } = require('@npmcli/config/lib/definitions') 131cb0ef41Sopenharmony_ciconst lstat = promisify(fs.lstat) 141cb0ef41Sopenharmony_ciconst readdir = promisify(fs.readdir) 151cb0ef41Sopenharmony_ciconst access = promisify(fs.access) 161cb0ef41Sopenharmony_ciconst { R_OK, W_OK, X_OK } = fs.constants 171cb0ef41Sopenharmony_ci 181cb0ef41Sopenharmony_ciconst maskLabel = mask => { 191cb0ef41Sopenharmony_ci const label = [] 201cb0ef41Sopenharmony_ci if (mask & R_OK) { 211cb0ef41Sopenharmony_ci label.push('readable') 221cb0ef41Sopenharmony_ci } 231cb0ef41Sopenharmony_ci 241cb0ef41Sopenharmony_ci if (mask & W_OK) { 251cb0ef41Sopenharmony_ci label.push('writable') 261cb0ef41Sopenharmony_ci } 271cb0ef41Sopenharmony_ci 281cb0ef41Sopenharmony_ci if (mask & X_OK) { 291cb0ef41Sopenharmony_ci label.push('executable') 301cb0ef41Sopenharmony_ci } 311cb0ef41Sopenharmony_ci 321cb0ef41Sopenharmony_ci return label.join(', ') 331cb0ef41Sopenharmony_ci} 341cb0ef41Sopenharmony_ci 351cb0ef41Sopenharmony_ciconst subcommands = [ 361cb0ef41Sopenharmony_ci { 371cb0ef41Sopenharmony_ci groups: ['ping', 'registry'], 381cb0ef41Sopenharmony_ci title: 'npm ping', 391cb0ef41Sopenharmony_ci cmd: 'checkPing', 401cb0ef41Sopenharmony_ci }, { 411cb0ef41Sopenharmony_ci groups: ['versions'], 421cb0ef41Sopenharmony_ci title: 'npm -v', 431cb0ef41Sopenharmony_ci cmd: 'getLatestNpmVersion', 441cb0ef41Sopenharmony_ci }, { 451cb0ef41Sopenharmony_ci groups: ['versions'], 461cb0ef41Sopenharmony_ci title: 'node -v', 471cb0ef41Sopenharmony_ci cmd: 'getLatestNodejsVersion', 481cb0ef41Sopenharmony_ci }, { 491cb0ef41Sopenharmony_ci groups: ['registry'], 501cb0ef41Sopenharmony_ci title: 'npm config get registry', 511cb0ef41Sopenharmony_ci cmd: 'checkNpmRegistry', 521cb0ef41Sopenharmony_ci }, { 531cb0ef41Sopenharmony_ci groups: ['environment'], 541cb0ef41Sopenharmony_ci title: 'git executable in PATH', 551cb0ef41Sopenharmony_ci cmd: 'getGitPath', 561cb0ef41Sopenharmony_ci }, { 571cb0ef41Sopenharmony_ci groups: ['environment'], 581cb0ef41Sopenharmony_ci title: 'global bin folder in PATH', 591cb0ef41Sopenharmony_ci cmd: 'getBinPath', 601cb0ef41Sopenharmony_ci }, { 611cb0ef41Sopenharmony_ci groups: ['permissions', 'cache'], 621cb0ef41Sopenharmony_ci title: 'Perms check on cached files', 631cb0ef41Sopenharmony_ci cmd: 'checkCachePermission', 641cb0ef41Sopenharmony_ci windows: false, 651cb0ef41Sopenharmony_ci }, { 661cb0ef41Sopenharmony_ci groups: ['permissions'], 671cb0ef41Sopenharmony_ci title: 'Perms check on local node_modules', 681cb0ef41Sopenharmony_ci cmd: 'checkLocalModulesPermission', 691cb0ef41Sopenharmony_ci windows: false, 701cb0ef41Sopenharmony_ci }, { 711cb0ef41Sopenharmony_ci groups: ['permissions'], 721cb0ef41Sopenharmony_ci title: 'Perms check on global node_modules', 731cb0ef41Sopenharmony_ci cmd: 'checkGlobalModulesPermission', 741cb0ef41Sopenharmony_ci windows: false, 751cb0ef41Sopenharmony_ci }, { 761cb0ef41Sopenharmony_ci groups: ['permissions'], 771cb0ef41Sopenharmony_ci title: 'Perms check on local bin folder', 781cb0ef41Sopenharmony_ci cmd: 'checkLocalBinPermission', 791cb0ef41Sopenharmony_ci windows: false, 801cb0ef41Sopenharmony_ci }, { 811cb0ef41Sopenharmony_ci groups: ['permissions'], 821cb0ef41Sopenharmony_ci title: 'Perms check on global bin folder', 831cb0ef41Sopenharmony_ci cmd: 'checkGlobalBinPermission', 841cb0ef41Sopenharmony_ci windows: false, 851cb0ef41Sopenharmony_ci }, { 861cb0ef41Sopenharmony_ci groups: ['cache'], 871cb0ef41Sopenharmony_ci title: 'Verify cache contents', 881cb0ef41Sopenharmony_ci cmd: 'verifyCachedFiles', 891cb0ef41Sopenharmony_ci windows: false, 901cb0ef41Sopenharmony_ci }, 911cb0ef41Sopenharmony_ci // TODO: 921cb0ef41Sopenharmony_ci // group === 'dependencies'? 931cb0ef41Sopenharmony_ci // - ensure arborist.loadActual() runs without errors and no invalid edges 941cb0ef41Sopenharmony_ci // - ensure package-lock.json matches loadActual() 951cb0ef41Sopenharmony_ci // - verify loadActual without hidden lock file matches hidden lockfile 961cb0ef41Sopenharmony_ci // group === '???' 971cb0ef41Sopenharmony_ci // - verify all local packages have bins linked 981cb0ef41Sopenharmony_ci // What is the fix for these? 991cb0ef41Sopenharmony_ci] 1001cb0ef41Sopenharmony_ciconst BaseCommand = require('../base-command.js') 1011cb0ef41Sopenharmony_ciclass Doctor extends BaseCommand { 1021cb0ef41Sopenharmony_ci static description = 'Check the health of your npm environment' 1031cb0ef41Sopenharmony_ci static name = 'doctor' 1041cb0ef41Sopenharmony_ci static params = ['registry'] 1051cb0ef41Sopenharmony_ci static ignoreImplicitWorkspace = false 1061cb0ef41Sopenharmony_ci static usage = [`[${subcommands.flatMap(s => s.groups) 1071cb0ef41Sopenharmony_ci .filter((value, index, self) => self.indexOf(value) === index) 1081cb0ef41Sopenharmony_ci .join('] [')}]`] 1091cb0ef41Sopenharmony_ci 1101cb0ef41Sopenharmony_ci static subcommands = subcommands 1111cb0ef41Sopenharmony_ci 1121cb0ef41Sopenharmony_ci // minimum width of check column, enough for the word `Check` 1131cb0ef41Sopenharmony_ci #checkWidth = 5 1141cb0ef41Sopenharmony_ci 1151cb0ef41Sopenharmony_ci async exec (args) { 1161cb0ef41Sopenharmony_ci log.info('Running checkup') 1171cb0ef41Sopenharmony_ci let allOk = true 1181cb0ef41Sopenharmony_ci 1191cb0ef41Sopenharmony_ci const actions = this.actions(args) 1201cb0ef41Sopenharmony_ci this.#checkWidth = actions.reduce((length, item) => 1211cb0ef41Sopenharmony_ci Math.max(item.title.length, length), this.#checkWidth) 1221cb0ef41Sopenharmony_ci 1231cb0ef41Sopenharmony_ci if (!this.npm.silent) { 1241cb0ef41Sopenharmony_ci this.output(['Check', 'Value', 'Recommendation/Notes'].map(h => this.npm.chalk.underline(h))) 1251cb0ef41Sopenharmony_ci } 1261cb0ef41Sopenharmony_ci // Do the actual work 1271cb0ef41Sopenharmony_ci for (const { title, cmd } of actions) { 1281cb0ef41Sopenharmony_ci const item = [title] 1291cb0ef41Sopenharmony_ci try { 1301cb0ef41Sopenharmony_ci item.push(true, await this[cmd]()) 1311cb0ef41Sopenharmony_ci } catch (err) { 1321cb0ef41Sopenharmony_ci item.push(false, err) 1331cb0ef41Sopenharmony_ci } 1341cb0ef41Sopenharmony_ci if (!item[1]) { 1351cb0ef41Sopenharmony_ci allOk = false 1361cb0ef41Sopenharmony_ci item[0] = this.npm.chalk.red(item[0]) 1371cb0ef41Sopenharmony_ci item[1] = this.npm.chalk.red('not ok') 1381cb0ef41Sopenharmony_ci item[2] = this.npm.chalk.magenta(String(item[2])) 1391cb0ef41Sopenharmony_ci } else { 1401cb0ef41Sopenharmony_ci item[1] = this.npm.chalk.green('ok') 1411cb0ef41Sopenharmony_ci } 1421cb0ef41Sopenharmony_ci if (!this.npm.silent) { 1431cb0ef41Sopenharmony_ci this.output(item) 1441cb0ef41Sopenharmony_ci } 1451cb0ef41Sopenharmony_ci } 1461cb0ef41Sopenharmony_ci 1471cb0ef41Sopenharmony_ci if (!allOk) { 1481cb0ef41Sopenharmony_ci if (this.npm.silent) { 1491cb0ef41Sopenharmony_ci /* eslint-disable-next-line max-len */ 1501cb0ef41Sopenharmony_ci throw new Error('Some problems found. Check logs or disable silent mode for recommendations.') 1511cb0ef41Sopenharmony_ci } else { 1521cb0ef41Sopenharmony_ci throw new Error('Some problems found. See above for recommendations.') 1531cb0ef41Sopenharmony_ci } 1541cb0ef41Sopenharmony_ci } 1551cb0ef41Sopenharmony_ci } 1561cb0ef41Sopenharmony_ci 1571cb0ef41Sopenharmony_ci async checkPing () { 1581cb0ef41Sopenharmony_ci const tracker = log.newItem('checkPing', 1) 1591cb0ef41Sopenharmony_ci tracker.info('checkPing', 'Pinging registry') 1601cb0ef41Sopenharmony_ci try { 1611cb0ef41Sopenharmony_ci await ping({ ...this.npm.flatOptions, retry: false }) 1621cb0ef41Sopenharmony_ci return '' 1631cb0ef41Sopenharmony_ci } catch (er) { 1641cb0ef41Sopenharmony_ci if (/^E\d{3}$/.test(er.code || '')) { 1651cb0ef41Sopenharmony_ci throw er.code.slice(1) + ' ' + er.message 1661cb0ef41Sopenharmony_ci } else { 1671cb0ef41Sopenharmony_ci throw er.message 1681cb0ef41Sopenharmony_ci } 1691cb0ef41Sopenharmony_ci } finally { 1701cb0ef41Sopenharmony_ci tracker.finish() 1711cb0ef41Sopenharmony_ci } 1721cb0ef41Sopenharmony_ci } 1731cb0ef41Sopenharmony_ci 1741cb0ef41Sopenharmony_ci async getLatestNpmVersion () { 1751cb0ef41Sopenharmony_ci const tracker = log.newItem('getLatestNpmVersion', 1) 1761cb0ef41Sopenharmony_ci tracker.info('getLatestNpmVersion', 'Getting npm package information') 1771cb0ef41Sopenharmony_ci try { 1781cb0ef41Sopenharmony_ci const latest = (await pacote.manifest('npm@latest', this.npm.flatOptions)).version 1791cb0ef41Sopenharmony_ci if (semver.gte(this.npm.version, latest)) { 1801cb0ef41Sopenharmony_ci return `current: v${this.npm.version}, latest: v${latest}` 1811cb0ef41Sopenharmony_ci } else { 1821cb0ef41Sopenharmony_ci throw `Use npm v${latest}` 1831cb0ef41Sopenharmony_ci } 1841cb0ef41Sopenharmony_ci } finally { 1851cb0ef41Sopenharmony_ci tracker.finish() 1861cb0ef41Sopenharmony_ci } 1871cb0ef41Sopenharmony_ci } 1881cb0ef41Sopenharmony_ci 1891cb0ef41Sopenharmony_ci async getLatestNodejsVersion () { 1901cb0ef41Sopenharmony_ci // XXX get the latest in the current major as well 1911cb0ef41Sopenharmony_ci const current = process.version 1921cb0ef41Sopenharmony_ci const currentRange = `^${current}` 1931cb0ef41Sopenharmony_ci const url = 'https://nodejs.org/dist/index.json' 1941cb0ef41Sopenharmony_ci const tracker = log.newItem('getLatestNodejsVersion', 1) 1951cb0ef41Sopenharmony_ci tracker.info('getLatestNodejsVersion', 'Getting Node.js release information') 1961cb0ef41Sopenharmony_ci try { 1971cb0ef41Sopenharmony_ci const res = await fetch(url, { method: 'GET', ...this.npm.flatOptions }) 1981cb0ef41Sopenharmony_ci const data = await res.json() 1991cb0ef41Sopenharmony_ci let maxCurrent = '0.0.0' 2001cb0ef41Sopenharmony_ci let maxLTS = '0.0.0' 2011cb0ef41Sopenharmony_ci for (const { lts, version } of data) { 2021cb0ef41Sopenharmony_ci if (lts && semver.gt(version, maxLTS)) { 2031cb0ef41Sopenharmony_ci maxLTS = version 2041cb0ef41Sopenharmony_ci } 2051cb0ef41Sopenharmony_ci 2061cb0ef41Sopenharmony_ci if (semver.satisfies(version, currentRange) && semver.gt(version, maxCurrent)) { 2071cb0ef41Sopenharmony_ci maxCurrent = version 2081cb0ef41Sopenharmony_ci } 2091cb0ef41Sopenharmony_ci } 2101cb0ef41Sopenharmony_ci const recommended = semver.gt(maxCurrent, maxLTS) ? maxCurrent : maxLTS 2111cb0ef41Sopenharmony_ci if (semver.gte(process.version, recommended)) { 2121cb0ef41Sopenharmony_ci return `current: ${current}, recommended: ${recommended}` 2131cb0ef41Sopenharmony_ci } else { 2141cb0ef41Sopenharmony_ci throw `Use node ${recommended} (current: ${current})` 2151cb0ef41Sopenharmony_ci } 2161cb0ef41Sopenharmony_ci } finally { 2171cb0ef41Sopenharmony_ci tracker.finish() 2181cb0ef41Sopenharmony_ci } 2191cb0ef41Sopenharmony_ci } 2201cb0ef41Sopenharmony_ci 2211cb0ef41Sopenharmony_ci async getBinPath (dir) { 2221cb0ef41Sopenharmony_ci const tracker = log.newItem('getBinPath', 1) 2231cb0ef41Sopenharmony_ci tracker.info('getBinPath', 'Finding npm global bin in your PATH') 2241cb0ef41Sopenharmony_ci if (!process.env.PATH.includes(this.npm.globalBin)) { 2251cb0ef41Sopenharmony_ci throw new Error(`Add ${this.npm.globalBin} to your $PATH`) 2261cb0ef41Sopenharmony_ci } 2271cb0ef41Sopenharmony_ci return this.npm.globalBin 2281cb0ef41Sopenharmony_ci } 2291cb0ef41Sopenharmony_ci 2301cb0ef41Sopenharmony_ci async checkCachePermission () { 2311cb0ef41Sopenharmony_ci return this.checkFilesPermission(this.npm.cache, true, R_OK) 2321cb0ef41Sopenharmony_ci } 2331cb0ef41Sopenharmony_ci 2341cb0ef41Sopenharmony_ci async checkLocalModulesPermission () { 2351cb0ef41Sopenharmony_ci return this.checkFilesPermission(this.npm.localDir, true, R_OK | W_OK, true) 2361cb0ef41Sopenharmony_ci } 2371cb0ef41Sopenharmony_ci 2381cb0ef41Sopenharmony_ci async checkGlobalModulesPermission () { 2391cb0ef41Sopenharmony_ci return this.checkFilesPermission(this.npm.globalDir, false, R_OK) 2401cb0ef41Sopenharmony_ci } 2411cb0ef41Sopenharmony_ci 2421cb0ef41Sopenharmony_ci async checkLocalBinPermission () { 2431cb0ef41Sopenharmony_ci return this.checkFilesPermission(this.npm.localBin, false, R_OK | W_OK | X_OK, true) 2441cb0ef41Sopenharmony_ci } 2451cb0ef41Sopenharmony_ci 2461cb0ef41Sopenharmony_ci async checkGlobalBinPermission () { 2471cb0ef41Sopenharmony_ci return this.checkFilesPermission(this.npm.globalBin, false, X_OK) 2481cb0ef41Sopenharmony_ci } 2491cb0ef41Sopenharmony_ci 2501cb0ef41Sopenharmony_ci async checkFilesPermission (root, shouldOwn, mask, missingOk) { 2511cb0ef41Sopenharmony_ci let ok = true 2521cb0ef41Sopenharmony_ci 2531cb0ef41Sopenharmony_ci const tracker = log.newItem(root, 1) 2541cb0ef41Sopenharmony_ci 2551cb0ef41Sopenharmony_ci try { 2561cb0ef41Sopenharmony_ci const uid = process.getuid() 2571cb0ef41Sopenharmony_ci const gid = process.getgid() 2581cb0ef41Sopenharmony_ci const files = new Set([root]) 2591cb0ef41Sopenharmony_ci for (const f of files) { 2601cb0ef41Sopenharmony_ci tracker.silly('checkFilesPermission', f.slice(root.length + 1)) 2611cb0ef41Sopenharmony_ci const st = await lstat(f).catch(er => { 2621cb0ef41Sopenharmony_ci // if it can't be missing, or if it can and the error wasn't that it was missing 2631cb0ef41Sopenharmony_ci if (!missingOk || er.code !== 'ENOENT') { 2641cb0ef41Sopenharmony_ci ok = false 2651cb0ef41Sopenharmony_ci tracker.warn('checkFilesPermission', 'error getting info for ' + f) 2661cb0ef41Sopenharmony_ci } 2671cb0ef41Sopenharmony_ci }) 2681cb0ef41Sopenharmony_ci 2691cb0ef41Sopenharmony_ci tracker.completeWork(1) 2701cb0ef41Sopenharmony_ci 2711cb0ef41Sopenharmony_ci if (!st) { 2721cb0ef41Sopenharmony_ci continue 2731cb0ef41Sopenharmony_ci } 2741cb0ef41Sopenharmony_ci 2751cb0ef41Sopenharmony_ci if (shouldOwn && (uid !== st.uid || gid !== st.gid)) { 2761cb0ef41Sopenharmony_ci tracker.warn('checkFilesPermission', 'should be owner of ' + f) 2771cb0ef41Sopenharmony_ci ok = false 2781cb0ef41Sopenharmony_ci } 2791cb0ef41Sopenharmony_ci 2801cb0ef41Sopenharmony_ci if (!st.isDirectory() && !st.isFile()) { 2811cb0ef41Sopenharmony_ci continue 2821cb0ef41Sopenharmony_ci } 2831cb0ef41Sopenharmony_ci 2841cb0ef41Sopenharmony_ci try { 2851cb0ef41Sopenharmony_ci await access(f, mask) 2861cb0ef41Sopenharmony_ci } catch (er) { 2871cb0ef41Sopenharmony_ci ok = false 2881cb0ef41Sopenharmony_ci const msg = `Missing permissions on ${f} (expect: ${maskLabel(mask)})` 2891cb0ef41Sopenharmony_ci tracker.error('checkFilesPermission', msg) 2901cb0ef41Sopenharmony_ci continue 2911cb0ef41Sopenharmony_ci } 2921cb0ef41Sopenharmony_ci 2931cb0ef41Sopenharmony_ci if (st.isDirectory()) { 2941cb0ef41Sopenharmony_ci const entries = await readdir(f).catch(er => { 2951cb0ef41Sopenharmony_ci ok = false 2961cb0ef41Sopenharmony_ci tracker.warn('checkFilesPermission', 'error reading directory ' + f) 2971cb0ef41Sopenharmony_ci return [] 2981cb0ef41Sopenharmony_ci }) 2991cb0ef41Sopenharmony_ci for (const entry of entries) { 3001cb0ef41Sopenharmony_ci files.add(resolve(f, entry)) 3011cb0ef41Sopenharmony_ci } 3021cb0ef41Sopenharmony_ci } 3031cb0ef41Sopenharmony_ci } 3041cb0ef41Sopenharmony_ci } finally { 3051cb0ef41Sopenharmony_ci tracker.finish() 3061cb0ef41Sopenharmony_ci if (!ok) { 3071cb0ef41Sopenharmony_ci throw ( 3081cb0ef41Sopenharmony_ci `Check the permissions of files in ${root}` + 3091cb0ef41Sopenharmony_ci (shouldOwn ? ' (should be owned by current user)' : '') 3101cb0ef41Sopenharmony_ci ) 3111cb0ef41Sopenharmony_ci } else { 3121cb0ef41Sopenharmony_ci return '' 3131cb0ef41Sopenharmony_ci } 3141cb0ef41Sopenharmony_ci } 3151cb0ef41Sopenharmony_ci } 3161cb0ef41Sopenharmony_ci 3171cb0ef41Sopenharmony_ci async getGitPath () { 3181cb0ef41Sopenharmony_ci const tracker = log.newItem('getGitPath', 1) 3191cb0ef41Sopenharmony_ci tracker.info('getGitPath', 'Finding git in your PATH') 3201cb0ef41Sopenharmony_ci try { 3211cb0ef41Sopenharmony_ci return await which('git').catch(er => { 3221cb0ef41Sopenharmony_ci tracker.warn(er) 3231cb0ef41Sopenharmony_ci throw new Error("Install git and ensure it's in your PATH.") 3241cb0ef41Sopenharmony_ci }) 3251cb0ef41Sopenharmony_ci } finally { 3261cb0ef41Sopenharmony_ci tracker.finish() 3271cb0ef41Sopenharmony_ci } 3281cb0ef41Sopenharmony_ci } 3291cb0ef41Sopenharmony_ci 3301cb0ef41Sopenharmony_ci async verifyCachedFiles () { 3311cb0ef41Sopenharmony_ci const tracker = log.newItem('verifyCachedFiles', 1) 3321cb0ef41Sopenharmony_ci tracker.info('verifyCachedFiles', 'Verifying the npm cache') 3331cb0ef41Sopenharmony_ci try { 3341cb0ef41Sopenharmony_ci const stats = await cacache.verify(this.npm.flatOptions.cache) 3351cb0ef41Sopenharmony_ci const { badContentCount, reclaimedCount, missingContent, reclaimedSize } = stats 3361cb0ef41Sopenharmony_ci if (badContentCount || reclaimedCount || missingContent) { 3371cb0ef41Sopenharmony_ci if (badContentCount) { 3381cb0ef41Sopenharmony_ci tracker.warn('verifyCachedFiles', `Corrupted content removed: ${badContentCount}`) 3391cb0ef41Sopenharmony_ci } 3401cb0ef41Sopenharmony_ci 3411cb0ef41Sopenharmony_ci if (reclaimedCount) { 3421cb0ef41Sopenharmony_ci tracker.warn( 3431cb0ef41Sopenharmony_ci 'verifyCachedFiles', 3441cb0ef41Sopenharmony_ci `Content garbage-collected: ${reclaimedCount} (${reclaimedSize} bytes)` 3451cb0ef41Sopenharmony_ci ) 3461cb0ef41Sopenharmony_ci } 3471cb0ef41Sopenharmony_ci 3481cb0ef41Sopenharmony_ci if (missingContent) { 3491cb0ef41Sopenharmony_ci tracker.warn('verifyCachedFiles', `Missing content: ${missingContent}`) 3501cb0ef41Sopenharmony_ci } 3511cb0ef41Sopenharmony_ci 3521cb0ef41Sopenharmony_ci tracker.warn('verifyCachedFiles', 'Cache issues have been fixed') 3531cb0ef41Sopenharmony_ci } 3541cb0ef41Sopenharmony_ci tracker.info( 3551cb0ef41Sopenharmony_ci 'verifyCachedFiles', 3561cb0ef41Sopenharmony_ci `Verification complete. Stats: ${JSON.stringify(stats, null, 2)}` 3571cb0ef41Sopenharmony_ci ) 3581cb0ef41Sopenharmony_ci return `verified ${stats.verifiedContent} tarballs` 3591cb0ef41Sopenharmony_ci } finally { 3601cb0ef41Sopenharmony_ci tracker.finish() 3611cb0ef41Sopenharmony_ci } 3621cb0ef41Sopenharmony_ci } 3631cb0ef41Sopenharmony_ci 3641cb0ef41Sopenharmony_ci async checkNpmRegistry () { 3651cb0ef41Sopenharmony_ci if (this.npm.flatOptions.registry !== defaults.registry) { 3661cb0ef41Sopenharmony_ci throw `Try \`npm config set registry=${defaults.registry}\`` 3671cb0ef41Sopenharmony_ci } else { 3681cb0ef41Sopenharmony_ci return `using default registry (${defaults.registry})` 3691cb0ef41Sopenharmony_ci } 3701cb0ef41Sopenharmony_ci } 3711cb0ef41Sopenharmony_ci 3721cb0ef41Sopenharmony_ci output (row) { 3731cb0ef41Sopenharmony_ci const t = new Table({ 3741cb0ef41Sopenharmony_ci chars: { 3751cb0ef41Sopenharmony_ci top: '', 3761cb0ef41Sopenharmony_ci 'top-mid': '', 3771cb0ef41Sopenharmony_ci 'top-left': '', 3781cb0ef41Sopenharmony_ci 'top-right': '', 3791cb0ef41Sopenharmony_ci bottom: '', 3801cb0ef41Sopenharmony_ci 'bottom-mid': '', 3811cb0ef41Sopenharmony_ci 'bottom-left': '', 3821cb0ef41Sopenharmony_ci 'bottom-right': '', 3831cb0ef41Sopenharmony_ci left: '', 3841cb0ef41Sopenharmony_ci 'left-mid': '', 3851cb0ef41Sopenharmony_ci mid: '', 3861cb0ef41Sopenharmony_ci 'mid-mid': '', 3871cb0ef41Sopenharmony_ci right: '', 3881cb0ef41Sopenharmony_ci 'right-mid': '', 3891cb0ef41Sopenharmony_ci middle: ' ', 3901cb0ef41Sopenharmony_ci }, 3911cb0ef41Sopenharmony_ci style: { 3921cb0ef41Sopenharmony_ci 'padding-left': 0, 3931cb0ef41Sopenharmony_ci 'padding-right': 0, 3941cb0ef41Sopenharmony_ci // setting border here is not necessary visually since we've already 3951cb0ef41Sopenharmony_ci // zeroed out all the chars above, but without it cli-table3 will wrap 3961cb0ef41Sopenharmony_ci // some of the separator spaces with ansi codes which show up in 3971cb0ef41Sopenharmony_ci // snapshots. 3981cb0ef41Sopenharmony_ci border: 0, 3991cb0ef41Sopenharmony_ci }, 4001cb0ef41Sopenharmony_ci colWidths: [this.#checkWidth, 6], 4011cb0ef41Sopenharmony_ci }) 4021cb0ef41Sopenharmony_ci t.push(row) 4031cb0ef41Sopenharmony_ci this.npm.output(t.toString()) 4041cb0ef41Sopenharmony_ci } 4051cb0ef41Sopenharmony_ci 4061cb0ef41Sopenharmony_ci actions (params) { 4071cb0ef41Sopenharmony_ci return this.constructor.subcommands.filter(subcmd => { 4081cb0ef41Sopenharmony_ci if (process.platform === 'win32' && subcmd.windows === false) { 4091cb0ef41Sopenharmony_ci return false 4101cb0ef41Sopenharmony_ci } 4111cb0ef41Sopenharmony_ci if (params.length) { 4121cb0ef41Sopenharmony_ci return params.some(param => subcmd.groups.includes(param)) 4131cb0ef41Sopenharmony_ci } 4141cb0ef41Sopenharmony_ci return true 4151cb0ef41Sopenharmony_ci }) 4161cb0ef41Sopenharmony_ci } 4171cb0ef41Sopenharmony_ci} 4181cb0ef41Sopenharmony_ci 4191cb0ef41Sopenharmony_cimodule.exports = Doctor 420