// 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 SKIP = Symbol('SKIP') const isGlobalNpmUpdate = npm => { return npm.flatOptions.global && ['install', 'update'].includes(npm.command) && npm.argv.some(arg => /^npm(@|$)/.test(arg)) } // update check frequency const DAILY = 1000 * 60 * 60 * 24 const WEEKLY = DAILY * 7 // don't put it in the _cacache folder, just in npm's cache const lastCheckedFile = npm => resolve(npm.flatOptions.cache, '../_update-notifier-last-checked') const checkTimeout = async (npm, duration) => { const t = new Date(Date.now() - duration) const f = lastCheckedFile(npm) // if we don't have a file, then definitely check it. const st = await stat(f).catch(() => ({ mtime: t - 1 })) return t > st.mtime } const updateNotifier = 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 SKIP } // 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 checkTimeout(npm, duration))) { return null } // if they're currently using a prerelease, nudge to the next prerelease // otherwise, nudge to latest. const useColor = npm.logColor const mani = await pacote.manifest(`npm@${spec}`, { // always prefer latest, even if doing --tag=whatever on the cmd defaultTag: 'latest', ...npm.flatOptions, }).catch(() => null) // if pacote failed, give up if (!mani) { return null } 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` return message } // only update the notification timeout if we actually finished checking module.exports = async npm => { const notification = await updateNotifier(npm) // dont write the file if we skipped checking altogether if (notification === SKIP) { return null } // intentional. do not await this. it's a best-effort update. if this // fails, it's ok. might be using /dev/null as the cache or something weird // like that. writeFile(lastCheckedFile(npm), '').catch(() => {}) return notification }
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
completion | Folder | 0755 |
|
|
config | Folder | 0755 |
|
|
ansi-trim.js | File | 173 B | 0644 |
|
audit-error.js | File | 978 B | 0644 |
|
cmd-list.js | File | 2.46 KB | 0644 |
|
completion.sh | File | 1.85 KB | 0755 |
|
did-you-mean.js | File | 1.46 KB | 0644 |
|
display.js | File | 2.84 KB | 0644 |
|
error-message.js | File | 12.64 KB | 0644 |
|
exit-handler.js | File | 6.77 KB | 0644 |
|
explain-dep.js | File | 3.73 KB | 0644 |
|
explain-eresolve.js | File | 2.56 KB | 0644 |
|
format-bytes.js | File | 629 B | 0644 |
|
format-search-stream.js | File | 3.93 KB | 0644 |
|
get-identity.js | File | 740 B | 0644 |
|
is-windows.js | File | 233 B | 0644 |
|
log-file.js | File | 7.64 KB | 0644 |
|
log-shim.js | File | 1.68 KB | 0644 |
|
npm-usage.js | File | 1.95 KB | 0644 |
|
open-url-prompt.js | File | 1.54 KB | 0644 |
|
open-url.js | File | 1.17 KB | 0644 |
|
otplease.js | File | 1.23 KB | 0644 |
|
ping.js | File | 252 B | 0644 |
|
pulse-till-done.js | File | 412 B | 0644 |
|
queryable.js | File | 9.58 KB | 0644 |
|
read-user-info.js | File | 1.99 KB | 0644 |
|
reify-finish.js | File | 876 B | 0644 |
|
reify-output.js | File | 4.96 KB | 0644 |
|
replace-info.js | File | 807 B | 0644 |
|
tar.js | File | 3.73 KB | 0644 |
|
timers.js | File | 3.18 KB | 0644 |
|
update-notifier.js | File | 4.45 KB | 0644 |
|
validate-lockfile.js | File | 1023 B | 0644 |
|
web-auth.js | File | 558 B | 0644 |
|