All files update-notifier.js

98% Statements 49/50
100% Branches 45/45
75% Functions 3/4
100% Lines 49/49

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118        1x 1x 1x 1x 1x 1x 1x 1x   1x 24x           1x 1x   1x 22x   22x   22x   22x     20x 20x   2x       1x   25x     3x         22x 22x     22x 4x       22x     22x 2x         20x   20x           20x           20x 5x       15x 3x           12x 12x       12x       12x 12x 12x 12x 12x 12x 12x       12x   12x    
// print a banner telling the user to upgrade npm to latest
// but not in CI, and not if we're doing that already.
// Check daily for betas, and weekly otherwise.
 
const pacote = require('pacote')
const ciDetect = require('@npmcli/ci-detect')
const semver = require('semver')
const chalk = require('chalk')
const { promisify } = require('util')
const stat = promisify(require('fs').stat)
const writeFile = promisify(require('fs').writeFile)
const { resolve } = require('path')
 
const isGlobalNpmUpdate = npm => {
  return npm.flatOptions.global &&
    ['install', 'update'].includes(npm.command) &&
    npm.argv.includes('npm')
}
 
// update check frequency
const DAILY = 1000 * 60 * 60 * 24
const WEEKLY = DAILY * 7
 
const updateTimeout = async (npm, duration) => {
  const t = new Date(Date.now() - duration)
  // don't put it in the _cacache folder, just in npm's cache
  const f = resolve(npm.flatOptions.cache, '../_update-notifier-last-checked')
  // if we don't have a file, then definitely check it.
  const st = await stat(f).catch(() => ({ mtime: t - 1 }))
 
  if (t > st.mtime) {
    // best effort, if this fails, it's ok.
    // might be using /dev/null as the cache or something weird like that.
    await writeFile(f, '').catch(/* istanbul ignore next */ () => {})
    return true
  } else {
    return false
  }
}
 
const updateNotifier = module.exports = async (npm, spec = 'latest') => {
  // never check for updates in CI, when updating npm already, or opted out
  if (!npm.config.get('update-notifier') ||
      isGlobalNpmUpdate(npm) ||
      ciDetect()) {
    return null
  }
 
  // if we're on a prerelease train, then updates are coming fast
  // check for a new one daily.  otherwise, weekly.
  const { version } = npm
  const current = semver.parse(version)
 
  // if we're on a beta train, always get the next beta
  if (current.prerelease.length) {
    spec = `^${version}`
  }
 
  // while on a beta train, get updates daily
  const duration = spec !== 'latest' ? DAILY : WEEKLY
 
  // if we've already checked within the specified duration, don't check again
  if (!(await updateTimeout(npm, duration))) {
    return null
  }
 
  // if they're currently using a prerelease, nudge to the next prerelease
  // otherwise, nudge to latest.
  const useColor = npm.log.useColor()
 
  const mani = await pacote.manifest(`npm@${spec}`, {
    // always prefer latest, even if doing --tag=whatever on the cmd
    defaultTag: 'latest',
    ...npm.flatOptions
  })
 
  const latest = mani.version
 
  // if the current version is *greater* than latest, we're on a 'next'
  // and should get the updates from that release train.
  // Note that this isn't another http request over the network, because
  // the packument will be cached by pacote from previous request.
  if (semver.gt(version, latest) && spec === 'latest') {
    return updateNotifier(npm, `^${version}`)
  }
 
  // if we already have something >= the desired spec, then we're done
  if (semver.gte(version, latest)) {
    return null
  }
 
  // ok!  notify the user about this update they should get.
  // The message is saved for printing at process exit so it will not get
  // lost in any other messages being printed as part of the command.
  const update = semver.parse(mani.version)
  const type = update.major !== current.major ? 'major'
    : update.minor !== current.minor ? 'minor'
    : update.patch !== current.patch ? 'patch'
    : 'prerelease'
  const typec = !useColor ? type
    : type === 'major' ? chalk.red(type)
    : type === 'minor' ? chalk.yellow(type)
    : chalk.green(type)
  const oldc = !useColor ? current : chalk.red(current)
  const latestc = !useColor ? latest : chalk.green(latest)
  const changelog = `https://github.com/npm/cli/releases/tag/v${latest}`
  const changelogc = !useColor ? `<${changelog}>` : chalk.cyan(changelog)
  const cmd = `npm install -g npm@${latest}`
  const cmdc = !useColor ? `\`${cmd}\`` : chalk.green(cmd)
  const message = `\nNew ${typec} version of npm available! ` +
    `${oldc} -> ${latestc}\n` +
    `Changelog: ${changelogc}\n` +
    `Run ${cmdc} to update!\n`
  const messagec = !useColor ? message : chalk.bgBlack.white(message)
 
  return messagec
}